synchronized 底层实现原理?
使用及原理 synchronized 是由一对 monitorenter/monitorexit 指令实现的,monitor 对象是同步的基本实现单元。示例代码:
public class SynchronizedDemo { private Object object = new Object(); public void m(){ synchronized (object){ System.out.println("123"); } } }
1
2
3
4
5
6
7
8
9通过 javap -c -v SynchronizedDemo 可以得到以下字节码
java0 aload_0 1 getfield #3 <com/lupf/xxx/SynchronizedDemo.object> 4 dup 5 astore_1 6 monitorenter 7 getstatic #4 <java/lang/System.out> 10 ldc #5 <123> 12 invokevirtual #6 <java/io/PrintStream.println> 15 aload_1 16 monitorexit 17 goto 25 (+8) 20 astore_2 21 aload_1 22 monitorexit 23 aload_2 24 athrow 25 return
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17前面的是monitorenter,后面的是离开monitorexit,不难想象一个线程也执行同步代码块,首先要获取锁,而获取锁的过程就是monitorenter ,在执行完代码块之后,要释放锁,释放锁就是执行monitorexit指令。
为什么会有两个monitorexit呢? 这个主要是防止在同步代码块中线程因异常退出,而锁没有得到释放,这必然会造成死锁(等待的线程永远获取不到锁)。因此最后一个monitorexit是保证在异常情况下,锁也可以得到释放,避免死锁。 仅有ACC_SYNCHRONIZED这么一个标志,该标记表明线程进入该方法时,需要monitorenter,退出该方法时需要monitorexit。
synchronized可重入的原理 重入锁是指一个线程获取到该锁之后,该线程可以继续获得该锁。底层原理维护一个计数器,当线程获取该锁时,计数器加一,再次获得该锁时继续加一,释放锁时,计数器减一,当计数器值为0时,表明该锁未被任何线程所持有,其它线程可以竞争获取锁。
synchronized的优化 在 Java 6 之前,monitor 的实现完全是依靠操作系统内部的互斥锁,因为需要进行用户态到内核态的切换,所以同步操作是一个无差别的重量级操作,性能也很低。
但在 Java 6 的时候,Java 虚拟机 对此进行了大刀阔斧地改进,提供了三种不同的 monitor 实现,也就是常说的三种不同的锁:偏向锁(Biased Locking)、轻量级锁和重量级锁,大大改进了其性能。