一、什么是synchronized 关键字
synchronized 关键字可以保证被它修饰的方法或者代码块在任意时刻只有一个线程能够执行。
二、怎么使用 synchronized 关键字
- 修饰实例方法时,获取的是当前对象实例的锁
synchronized void method() {
//业务代码
}
- 修饰静态方法时,获取的是当前类Class的锁,因为静态方法不属于任何一个实例对象(不管new了多少个对象,都只有一份)。这里有点区别于修饰实例方法。
synchronized static void method() {
//业务代码
}
- 修饰代码块时,需要指定锁对象
synchronized(this|object)
synchronized(this) {
//业务代码
}
三、synchronized 关键字的底层原理
1、新建Demo类,synchronized 修饰了制定代码块
public class SynchronizedDemo {
public void method() {
synchronized (this) {
System.out.println("synchronized 代码块");
}
}
}
2、执行javap -c -s -v -l SynchronizedDemo.class
,查看底层字节码。
synchronized 同步代码块使用的是 monitorenter
和 monitorexit
指令,monitorenter
指向同步代码块开始位置,monitorexit
指向同步代码块结束位置(图中可以看到第19行还有一个monitorexit的原因,是处理程序出现异常的时候执行这个monitorexit来退出锁,如果程序没有异常直接在14行go to至22行就直接return了)。
synchronized 锁本质就是对对象监视器 monitor 的获取。
在执行monitorenter
时,会尝试获取对象的锁,如果锁的计数器为 0 则表示锁可以被获取,获取后将锁计数器设为 1 也就是加 1。如果获取对象锁失败,那当前线程就要阻塞等待,直到锁被另外一个线程释放为止。
对象锁的的拥有者线程才可以执行 monitorexit
指令来释放锁。在执行 monitorexit
指令后,将锁计数器设为 0,表明锁被释放,其他线程可以尝试获取锁。
四、JDK1.6 之后对 synchronized 关键字的优化
在 Java 早期版本中,synchronized
属于 重量级锁,效率低下,庆幸的是在 Java 6 之后 Java 官方对从 JVM 层面对 synchronized
较大优化,所以现在的 synchronized
锁效率也优化得很不错了。
JDK1.6 对锁的实现引入了大量的优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。