synchronized底层
- JDK早期的实现是重量级锁(向操作系统申请锁)。
- Hotspot实现(锁升级)
- 首先在锁的markword上记录第一个访问的线程ID,并没有上锁(偏向锁)。
- 如果有线程争用,升级为自旋锁。
- 默认旋10次之后升级为重量级锁,未获得锁的线程进入等待队列。
- 可参考ifeve.com/java-synchr…
引申:
- 在Hotspot锁只能升级,不能降级。
- 自旋锁只占用CPU,不访问操作系统。在用户态解决锁问题,不经过内核态,加解锁效率比重量级锁效率高。
- 执行时间长以及线程多的时候尽量使用重量级锁。
synchronized优化
- 锁细化:同步代码块中的语句越少越好。
- 锁粗化:如果一个方法中加了很多锁,这时把整个方法上锁性能会提高很多。
- 锁对象要加final。
volatile
-
保证线程可见性
- 所有线程共享堆内存,另外每个线程都有自己的工作内存。当线程要访问堆内存中的数据时,会把堆内存中的数据copy到线程的工作内存,堆该变量的改变是在工作内存的数据进行改变。线程堆变量的更改,没有及时的反应到其他的线程,导致线程之间数据不可见。
-
禁止指令重排序
- CPU为了提高执行效率,会并发的执行指令。使用volatile之后,CPU不会进行重排序。
- DCL单例
package designPatterns; public class DCLSingleTon { private volatile static DCLSingleTon dclSingleTon; private DCLSingleTon() { } public static DCLSingleTon getInstance() { // 外层的判断不能去掉,可以显著提高效率 if (dclSingleTon == null) { synchronized (DCLSingleTon.class) { if (dclSingleTon == null) { /** * 经过编译之后分为三步 * 1.分配内存,给变量赋默认值 * 2.成员变量初始化 * 3.把内存赋值给dclSingleTon * 如果发生指令重排序,可能会发生2和3交换 * 这时dclSingleTon已经不是null,新来的线程就会直接获得未初始化成员变量的instance */ dclSingleTon = new DCLSingleTon(); } } } return dclSingleTon; } }
- CPU为了提高执行效率,会并发的执行指令。使用volatile之后,CPU不会进行重排序。
-
不能保证原子性
CAS(Compare And Swap)
-
cas(V, Expected, NewValue)
if V == Expected V = NewValue otherwise try again or fail- concurrent包中的cas是CPU原语支持,执行过程不会被打断
-
ABA问题
- 当前线程的CAS操作无法分辨当前V值是否发生过变化。比如在你非常渴的情况下发现一个盛满水的杯子,一饮而尽之后再给被子里重新倒满水。你离开之后,杯子的主人回来看到杯子还是盛满水,他无法确定这杯水是否被人喝过重新倒满。
- 可以通过加版本号解决。
-
LongAdder与AtomicLong比较
- 两者都是使用的CAS操作来进行增减。
- 不通电在于LongAdder内部是分段锁。