线程安全与锁优化
当多个线程同时访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都会获得正确的结果,那就称这个对象是线程安全的。
实现线程安全
互斥同步,(互斥实现同步)
-
synchronized
关键字synchronized
关键字经过javac
编译后,会在同步块前后形成monitorenter
和monitorexit
两条字节码指令monitorenter
指令–》线程尝试获取对象的锁,如果对象没被锁定,或者当前线程已经持有对象的锁,锁的计数器的值加1;monitorexit
,锁的计数器的值减1。一旦锁的计数器的值为0,锁释放;获取对象锁失败,当前线程被阻塞等待,直到请求锁定的对象被持有它的线程释放。
-
Lock
接口ReentrantLock
,与Synchronized
相比,增加了一些高级功能:(1)等待可中断
(2)可实现公平锁
(3)锁绑定多个条件
非阻塞同步(基于冲突检测的乐观并发策略)
CAS
锁优化
-
自旋锁与自适应锁
- 锁时间很短,让请求锁的线程等待,执行忙循环(自旋),不放弃处理器执行时间,避免线程切换的开销。 使用:
-XX:+UseSpinning
开启;自旋次数默认10次,可使用参数-XX:PreBlockSpin
自行修改。 - 自适应锁是对自旋锁的优化,自旋的时间由前一次在同一个锁的自旋时间和获取锁的线程的状态来决定
- 锁时间很短,让请求锁的线程等待,执行忙循环(自旋),不放弃处理器执行时间,避免线程切换的开销。 使用:
-
锁消除
对不可能存在共享数据竞争的锁进行消除,在编译器即时编译之后,会忽略同步措施直接执行
-
锁粗化
在循环内对同一个对象反复进行加锁和解锁,频繁的互斥同步操作消耗性能,这种场景直接将锁粗化到外部,加一次锁即可
-
轻量级锁
对于绝大部分锁,在整个同步周期内不存在竞争,通过使用CAS操作避免使用互斥量的开销
代码进入同步块,同步对象未被锁定(锁标志位"01"),虚拟机在当前线程的栈帧中建立一个名为锁记录的空间,用于存储目前对象
Mark Word
的拷贝,然后虚拟机使用CAS
操作尝试将Mark Word
更新为指向锁空间的指针。操作成功即标识线程成功获取锁,对象的锁标志位更新为"00",标识对象处于轻量级锁状态;
如果操作失败,意味着有其他线程竞争,虚拟机会先检查
Mark Word
是否指向当前线程栈帧,如果是,那么表名当前线程已经获取到该锁;否则说明锁对象已经被其他线程抢占。如果存在两个以上的线程竞争同一把锁,这个时候锁膨胀为重量级锁,锁标志变为"10"。
-
偏向锁
消除数据再无竞争的情况下的同步操作
启用参数:-XX:UseBiasedLocking
当锁对象第一次被线程获取时,虚拟机将
Mark Word
标识为"01",偏向模式设置为"1";同时使用CAS
操作将获取锁的线程id记录到Mark Word
;如果CAS成功,持有偏向锁的线程以后每次进入这个锁的相关同步块的时候,虚拟机可以不再执行同步操作;一旦出现另外的线程尝试获取锁,会结束偏向模式,如果当前锁对象未被锁定,撤销偏向,偏向模式设置为"0";标志位会变成"01"(未锁定状态)或者"00"(轻量级锁状态)。