使用synchronized
的缘由在于:它可以确保多个线程在同一时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性。bash
在JDK 1.6
以前,synchronized
的实现是基于对象上的监视器,这也被称为重量锁。默认状况下,每个对象都有一个关联的Monitor
,而每一个Monitor
包含了一个EntryCount
计数器,它是synchronized
实现可重入的关键。优化
在JDK 1.6
以后,对锁进行了一系列优化的措施,经过引入自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减小锁操做的开销。this
这些优化措施最终的目的是减小锁操做的开销,然而它所改变的只是锁的实现方式,可是加锁和解锁这一基本原则是没有改变的。这篇文章主要是介绍synchronized
的使用,所以,在后面的介绍中,咱们仍是按照比较容易理解的重量锁的方式进行分析,在以后的文章中,咱们再来谈一下优化后的实现策略。spa
当一个线程执行某个对象的同步方法或者代码块时,会先检查这个对象所关联的Monitor's EntryCount
是否为0
:线程
EntryCount
为0
,那么该线程就会将Monitor’s EntryCount
设置为1
,并成为该Monitor
的全部者,接着执行该方法或者代码块中的语句。EntryCount
不为0
,这时会去检查对象所关联的Monitor
的持有者是哪个线程:Monitor
的线程就是当前正在尝试获取Monitor
的线程,那么将EntryCount
的数值加1
,继续执行方法或者代码块中的语句。Monitor
的是其它的线程,那么该线程进入阻塞状态,直到EntryCount
的数值变为0
。当一个线程从同步方法或者代码块退出时,会将EntryCount
减1
,若是EntryCount
变为0
,那么该线程会释放它所持有的Monitor
。以前那些阻塞在synchronized
的线程会尝试去获取Monitor
,成功获取Monitor
的线程能够进入同步方法或者代码块。code
对于synchronized
的使用,咱们有两种分类方法:对象
Monitor
关联的对象分类。不少介绍synchronized
的文章,都是经过使用场景进行分类的,通常来讲能够分为以下四种使用场景,而每种场景下根据Monitor
所关联的对象不一样,又会衍生出另外的用法:同步
//静态方法,使用的是Class类锁
synchronized public static void staticMethod() {}
复制代码
private static final byte[] mStaticLockByte = new byte[1];
//静态方法代码块1,使用的是Class类锁
public static void staticBlock1() {
synchronized (SynchronizedObject.class) {}
}
//静态方法代码块2,使用的是内部静态变量锁
public static void staticBlock2() {
synchronized (mStaticLockByte) {}
}
复制代码
//普通方法,使用的是调用该方法的对象锁
synchronized public void method() {}
复制代码
private static final byte[] mStaticLockByte = new byte[1];
private final byte[] mLockByte = new byte[1];
//普通方法代码块1,使用的是Class类锁
public void block1() {
synchronized (SynchronizedObject.class) {}
}
//普通方法代码块2,使用的是mLockByte的变量锁
public void block2() {
synchronized (mLockByte) {} //变量须要声明为final
}
//普通方法代码块3,使用的是mStaticLockByte的变量锁
public void block3() {
synchronized (mStaticLockByte) {}
}
//普通方法代码块4,使用的是调用该方法的对象锁
public void block4() {
synchronized (this) {}
}
复制代码
根据使用场景进行分类,主要是为了让你们知道如何使用synchronized
关键字,然而要真正地理解synchronized
,就须要结合第二节谈到的synchronized
原理,其实3.1
中谈到的多种场景,都是和Monitor
有关,那么从和Monitor
关联的对象来看,咱们从新对3.1
中的8
种场景从新进行分类:it
Class
对象:SynchronizedObject.class
SynchronizedObject.class
this
mStaticLockByte
mStaticLockByte
mLockByte
若是使用场景属于上面的同一个分类当中,那么才有可能产生线程阻塞在synchronized
关键字的状况,举一个例子,若是A
线程经过静态方法访问(分类一)而且没有从该方法退出:io
B
线程是经过一个对象的普通方法来访问(分类二),那么是不会阻塞的,这是由于调用该方法的对象所关联的Monitor
没有被持有。B
线程使用的是静态方法代码块来访问,而该静态方法代码块使用的是SynchronizedObject.class
来修饰(分类一),因为这两种使用场景是属于同一个分类,那么就会B
线程就会进入阻塞状态,这是由于SynchronizedObject
类所关联的Monitor
已经被A
线程持有了。从表面上来看,synchronized
的使用能够简单地分为同步方法和同步代码块,可是究竟在什么状况下会致使一个线程在synchronized
上阻塞,则须要分析synchronized
方法所尝试获取的Monitor
的是否已经被其它线程持有了。