首页 > 深入理解Java虚拟机 > 正文

[总结]-第十三章 线程安全与锁优化

佛若2018-09-120人围观

[总结]-第十三章 线程安全与锁优化

一、线程安全

摘录:当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的。(见P386)

  • 什么都不用关心;
  • 什么措施都不用添加;
  • 最终结果都是正确的。

二、线程安全程度

  • 不可变:final关键字修饰(如String枚举类java.lang.Number部分子类

  • 绝对线程安全:synchronized(不管运行时环境如何,调用者都不需要任何额外的同步措施)

  • 相对线程安全:大部分的线程安全类(VectorHashTableCollections.synchronizedCollection()

  • 线程兼容:对象本身并不线程安全,采用同步手段后可以安全的使用(如ArrayListHashMap

  • 线程对立:线程对立比较有害,尽量避免(Thread的suspend()resume()、System的setIn()setOut()

三、线程安全的实现

1、互斥同步(加锁 - 悲观锁)
  • synchronized
    编译后 -> monitorenter(锁计数器+1)、monitorexit(锁计数器-1)指令,计数器=0时,锁被释放。

  • ReentrantLock(可重入锁)
    lock()unlock()方法配合try/finally来使用

    不同点:ReentrantLock新增一些高级功能:

    • 等待可中断
    • 可实现公平锁
    • 锁可以绑定多个条件

      但是,如果synchronized能实现需求,则优先考虑使用synchronized。

2、非堵塞同步(乐观锁、冲突检测机制)
  1. CAS ( V, A, B )
  • V:变量的内存地址
  • A:旧的预期值
  • B:新值

定义:当且仅当V符合旧的预期值A时,处理器用新值B更新V的值,否则它就不执行更新。

该操作由 sun.misc.Unsafe 类里面的compareAndSwapInt()和compareAndSwapLong()等方法包装提供。

BUG:-> “ABA”问题,就是本来是A,改为了B,又被改为了A,但是CAS机制会认为没有被改动过。但不会影响程序并发的正确性,可以不理会,非要处理,可以通过“AtomicStampedReference”(有变量值的版本)来处理,或者直接使用传统的互斥同步(锁)。

3、无同步方案
  • 可重入代码(纯代码)
  • 线程本地存储:利用 java.lang.ThreadLocal 获取本地线程变量(Thread - ThreadLocalMap<ThreadLocal.threadLocalHashCode, 线程变量值>)

四、锁优化

  • 自旋锁/自适应自旋锁
  • 锁消除
    • 消除不存在共享数据竞争的锁
  • 锁粗化
    • 如果虚拟机探测到有这样一串零碎的操作都对同一个对象加锁,将会把加锁同步的范围扩展(粗化)到整个操作序列的外部(StringBuffer.append()每个方法加锁,但是循环使用时,会自动扩展到第一个append()操作之前直至最后一个append()操作之后)。
  • 轻量级锁
    • 在无竞争的情况下使用CAS操作去消除同步使用的互斥量。
    • 如果有两条以上的线程竞争同一个锁,那轻量级锁不再有效,膨胀为重量级锁。
  • 偏向锁
    • 在无竞争情况下消除整个同步,连CAS也不做;
    • 持有偏向锁的线程将永远不需要再进行同步;
    • 当有另外一个线程去尝试获取这个锁时,偏向模式就宣告解释。