从上一篇讲线程中,我们了解到了线程的各个状态,以及如何状态间的转换。此篇来温习下,线程的阻塞状态与运行态是怎样的转换过程。
synchronized
我们知道,多个线程相互配合的完成某一项工作,必然牵扯到对共享资源的竞争。而synchronized关键字便是有效实现多个线程间同步操作的利器,可以使共享资源在同一时间只能被一个线程享用,从而保证了线程对共享资源的可见性和排他性,实现线程安全。
synchronized,可以修饰方法,使方法成为同步方法,或者修饰代码块,使之成为同步块,二者的作用都是一样。举个简单例子,然后剖析:
public class TestSynchronized {
public static void main(String[] args) {
synchronized (TestSynchronized.class){
}
}
public static synchronized void a(){
}
}
执行javap,查看该代码的字节码:
main方法中的synchronized方法,转换为monitorenter和monitorexit指令。而同步方法中加了ACC_SYNCHRONIZED来标识。
这两种方式的实质都是通过一个对象的监视器(monitor)来进行的,执行方法的线程必须获取到了对象的monitor才能进入synchronized修饰的方法体内,也就是同一时刻只能有一个线程获取到monitor。如果线程未获取到monitor,那么将进入阻塞状态,阻塞在同步块或方法的入口。
线程与monitor
线程与monitor的关系如下:
任何线程获取synchronized保护的对象资源时,必须先获得对象的监视器。若获取monitor失败,则进入同步队列,线程变为阻塞状态。当获取了对象资源的线程释放了monitor后,会唤醒阻塞在同步队列中等待的线程,让队首的线程重新尝试获取monitor。
当程序线程进入monitorenter指令时,即占用了monitor锁,monitor就处于锁定状态。过程如下:
- 如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。
- 如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1。
- 如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。
当程序执行到monitorexit时,monitor的进入数减1,如果减1后monitor的进入数是0,该线程释放monitor的所有权,其他线程尝试获取monitor所有权。
所以synchronized的特性之一–可重入性,体现在monitor进入数的递增,大于1表示已经重入了多锁。