引言
在多线程并发访问下,如何确保数据的正确性至关重要。最常用的保证线程安全的方式是通过加锁,加锁后多线程并发访问转变成串行访问,这由此也引发了加锁后性能下降,线程切换频繁等问题。那么,究竟Synchronized内部是如何实现同步互斥机制的呢?
Synchronized是什么
在Java并发编程里,线程安全是最最需要关注的问题。那什么是线程安全呢?可以简单的理解为,线程安全就是多线程访问下,能正确的读取和写入共享数据的方式;那么,Synchronized就是为了提供线程安全的一种常用的实现机制。
Synchronized也称为同步互斥的机制,通过在方法中或代码块增加Synchronized关键字,JVM会将多线程并行访问模式转变为串行排队访问模型,保证同一个时间内只有唯一的线程在执行代码和访问共享数据。
Synchronized实现原理
Synchronized底层实现
Synchronized关键字最常用于修饰方法和代码块
// 同步块
synchronized(obj) {...};
// 同步方法
public synchronized void method(){...}
通过对生成的class文件执行javap -verbose命令, 可以看到synchronized的字节码实现


我们可以看到,同步块是通过分别插入monitorenter
和monitoreixt
俩指令,而同步方法则采用了ACC_SYNCHRONIZED
标识位来标示为一个同步方法。这两种同步虽然在表现形式上存在差异,但实现上都是采用Monitor模式来实现的。
Monitor模式
Monitor模式提供了线程间互斥与协同的同步实现机制,互斥执行确保同一时间点内只有一个线程操作共享内存数据,配合wait/notify/notifyAll来协调线程进行有序的工作。
原理图

可以看到Monitor分别有三个不同的区域组成,分别是
Entry Set: 进入synchronized,尝试获取monitor
对象的线程集合区域。
The Owner:持有monitor
对象线程执行的同步Monitor区域。
Wait Set:调用wait方法后,被挂起等待的线程区域。
线程在Monitor模式下状态转变的过程可以分为以下几个步骤
1.enter -- 线程进入Entry Set区域,如果区域内有其他的线程,那么进行排队等待;否则,进入步骤2.
2.acquire in Entry Set -- 线程尝试获取monitor
对象,如果线程获取锁成功,进入步骤3;否则,线程将在Entry Set阻塞等待。
3.release and exit -- 若持有monitor
对象的线程执行完整段Monitor区域代码后,并且没有执行wait/notify的指令。那么,线程就释放monitor
对象,并退出Owner区域。
4.release -- 若持有对象锁的线程执行到wait指令,线程将释放掉占有monitor
对象的锁资源,并进入Wait Set区域,进入这个区域的线程将处于休眠状态等待再次被唤醒进入到Owner区域内。
5.acquire in Wait Set -- 当Owner区域中的线程调用了notify/notifyAll方法后,会唤醒单个或所有在Wait Set区域中的线程重新竞争获取monitor
锁资源。
注:当执行notify方法时,不当当会引起Wait Set内的线程争抢对象锁;同时争抢对象锁的还包括在Entry Set内等待的线程集合。Wait Set vs Entry Set(线程优先级越高,竞争成功得几率最大),竞争成功得线程,将成为对象锁的持有者,进入Owner区域。
网友评论