多线程应用与实践

详细介绍多线程的应用与实践

Posted by chengweii on November 5, 2017

Thread

线程内存模型

线程:表示程序的执行流程,是CPU调度执行的基本单位;线程有自己的程序计数器、寄存器、堆栈和帧。同一进程中的线程共用相同的地址空间,同时共享进程所拥有的内存和其他资源。

数据的共享与私有

线程状态

线程状态切换

线程安全

当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替执行,并且不需要额外的同步及在调用方法代码时不必做其他的协调,这个类的行为仍是正确的,那么称这个类是线程安全的。

并发问题

原子性问题

  • 竞争条件
    • 检查再运行
    • 复合操作 long和double的赋值、i++操作

      可见性问题

  • 无状态对象永远是线程安全的

    原子性

    原子类

  • AtomicBoolean、AtomicInteger、AtomicIntegerArray、AtomicLong、AtomicLongArray、AtomicReference、AtomicReferenceArray、AtomicMarkableReference、AtomicStampedReference、AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater等
    • CAS算法

      对象锁、类锁(特殊对象锁)
      自旋锁、阻塞锁、可重入锁

      同步(锁控制)

      同步操作不止是控制线程执行的顺序,还会自动从主内存中更新工作内存中的共享变量的数据。

      synchronized、wait、notify、notifyAll

      wait隶属于对象,该操作会释放锁,将当前线程放入对象的等待池中

      ReentrantLock与ReentrantReadWriteLock
      join(wait、notify实现)

      把指定的线程加入到当前线程之前执行,可以将两个交替执行的线程合并为顺序执行的线程。

      死锁

可见性

  • 非原子性读写问题
  • 代码重排序问题

    解决方案

    volatile

    volatile修饰的变量的获取会强制线程同步在主内存的变量数据到工作内存中。

    注意:实际中发现线程阻塞操作如:Thread.sleep()、IO、synchronized、等待另一个线程的计算结果等也会自动同步主内存的变量数据到工作内存中。

  • 未声明为volatile的long和double的变量访问数据可能不完整(由于这类变量的读写是非原子的在64位系统下)
    加锁

线程切换

  • IO等阻塞操作会使JVM将当前线程移出调度队列,直至IO结束,然而阻塞操作完成后,当前线程不一定会继续获得执行权,这时很可能会发生线程切换。

线程通信

线程间通信

线程内通信

ThreadLocal

InheritableThreadLocal

性能和可伸缩性

减少锁的竞争

减少锁的时间

  • 同步块的代码越少越好

    减小锁的粒度

  • ConcurrentHashMap hash bucket 16个锁 n%16

    减少上下文切换的开销

并发应用

线程池

###

多线程面试题

什么是线程安全?

当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替执行,并且不需要额外的同步及在调用方法代码时不必做其他的协调,这个类的行为仍是正确的,那么称这个类是线程安全的。

ConcurrentHashMap的原理?

SimpleDateFormat存在线程安全问题吗?

在设计工具类时为了效率,会将其设计为单例对象,但是DateFormat 和 SimpleDateFormat 类不都是线程安全的,在多线程环境下调用 format() 和 parse() 方法应该使用同步代码来避免问题(该方法中涉及操作对象成员变量calendar)。解决办法包括:

  • 需要的时候创建新实例
  • 使用同步:同步SimpleDateFormat对象
  • 使用ThreadLocal

参考文献

JAVA CAS原理深度分析
深入理解Java:SimpleDateFormat安全的时间格式化