Java虚拟机--线程安全与锁优化

时间:2022-12-27 18:45:42

线程安全

在Java中可以对各种操作共享数据分为以下5类:不可变、绝对线程安全、相对线程安全、线程兼容、线程对立。

1)不可变

用final修饰的基本变量、行为不会对自己的状态产生影响的对象(比如,把自己的状态设置为final的)、枚举类、java.lang.Nuncer的部分子类(Long,Double等的数值包装类)、BigInteger和BigDecimal等大数据类型。(AtomicInteger和AtomicLong并非不可变)

2)绝对线程安全

即使,一个对象的自身行为全部是同步的,在多线程环境下也不能保证线程的绝对安全。

3)相对线程安全

Java的所谓得线程安全的集合类大部分都是相对线程安全的,需要在调用端加上额外的同步操作。

4)线程兼容

对象本身并不是线程安全,而是通过在调用端正确使用同步手段保证对象的线程安全。如ArrayList、HashMap等。

5)线程对立

无论调用端采用哪种同步操作,都无法在多线程环境下并发使用的代码。

线程安全的实现方法(虚拟机层次)

1)互斥同步

互斥是实现同步的一种手段,临界区(Critical Section)、互斥量(Mutex)、信号量(Semaphone)都是互斥实现方式。

Syncronized同步块,编译后会在同步块前后,加入monitorenter、monitorexit指令。线程阻塞或唤醒时,需要内核调度,要进行用户态核心态的切换,很耗时。虚拟机会进行优化,如自旋等待。

ReentrantLock,也可实现同步功能(lock、unlock、配合try/finally完成),而且比Syncronized多了高级功能:等待可中断、可实现公平锁、锁可绑定多个条件。如下图所示:
Java虚拟机--线程安全与锁优化

2)非阻塞同步

乐观同步策略

3)无同步方案

  • 可重入代码块:例如不依赖存储在堆上的数据和公用的系统资源
  • 线程本地存储:ThreadLocal的实现

锁优化

1)自旋锁与自适应锁

自旋锁:为了防止同步造成的阻塞对性能的影响,让获取不到锁的线程先进行一个忙循环(自旋),暂时不放弃CPU,这就是自旋锁。
自适应锁:自旋时间不再固定,由历史情况计算时间(很智能啊~)

2)锁消除

编译器优化掉不需要的锁。

3)锁粗化

如果检测到短时间内对同一个对象反复加锁(比如,在一个循环里面有同步代码块),就会把加锁范围扩大(比如,就扩大到循环外围),这样就只有枷锁一次。

4)轻量级锁

对于绝大部分的锁,在整个同步周期都是不存在周期的,使用轻量级锁的话,在没有竞争时就不会有互斥的开销,若有竞争就会转化为重量级锁(在有竞争的情况下,轻量级锁比重量级锁更慢,有一个判断竞争的过程)。

5)偏向锁

当拥有偏向锁的对象第一次被一个线程获取锁的时候,对象就会置为“偏向模式”,持有偏向锁的线程以后每次进入这个锁得同步块时,虚拟机可以不用任何同步操作。直到,另一个线程尝试获取此锁时,“偏向模式”宣告结束。