关键字
java
synchronized
monitor
在多线程并发编程中,synchronized
是一个常见的同步手段。我们经常使用该关键字,来确保同一个方法、代码块在同一时刻只能被一个线程访问。
使用方式
synchronized
在实际使用中有如下几种方式:
- 同步 类
- 同步 方法
- 同步 代码块
public class SynchronizeExample {
/**
* 同步 静态类方法,锁作用于类
*/
public static synchronized String theClassStaticMethod() {
return "lockClass";
}
/**
* 同步 对象方法,锁作用于对象本身
*/
public static synchronized String theObjectMethod() {
return "lockObject";
}
/**
* 同步 类关键字,锁作用于类
*/
public String lockClass() {
synchronized (SynchronizeExample.class) {
return "lockClass";
}
}
/**
* 同步 this,锁作用于本身
*/
public String lockThis() {
synchronized (this) {
return "lockObject";
}
}
}
synchronized
控制同步的手段,实际上是通过锁机制来保证的,锁定方式包括:
锁定类对象(即class,因为class本身也是一种特殊的对象)、锁定实例对象。
其中同步类关键字、同步类静态方法都属于类锁定,同步对象方法、同步代码块属于对象锁定。
上边我们不断的提到锁,那synchronized
到底是怎么用的锁?这就要提出一个很重要的概念:Monitor
。
monitor
在java中,每一个对象都有一个monitor。可以将这个monitor理解为对象的令牌。monitor在同一时刻只能被一个线程锁持有。synchronized
正是利用monitor来达到线程同步的目的,确保同一时刻只能有一个线程在执行相关代码。
在JVM实现规范中,对monitor的描述如下:
每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
- 如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。
- 如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.
- 如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。
线程获取锁的流程机制如下:
关于wait set 部分,准备在其他文章中单独说明。这个和java Object 的wait notify机制更相关。

代码解析
上文我们提到,synchronized
使用monitor
来进行同步控制,那具体是如何使用的呢?
对上文中的代码,使用javap -v SynchronizeExample
查看字节码文件,可以看到如下图所示的两部分:
- 在同步代码块中,使用
monitorenter
和monitorexit
来进行锁的获取和释放。 - 在同步方法上,会在flag上使用
ACC_SYNCHRONIZED
进行标记。jvm在处理该关键字时,会自动在调用方法前,隐式的调用monitorenter
,在方法结束或者异常后,调用monitorexit
。
反编译内容
总结
通过上文分析,我们得出如下结论:
-
synchronized
能够实现对共享资源的同步访问,保证在一个时刻最多只有一个线程能够访问同步资源。 -
synchronized
是通过锁的机制来达到同步访问控制,其中锁包括类级别的和对象级别的。 - 在锁的实现上,是通过实例对象、类对象中的
monitor
,以及monitorenter
和monitorexit
来达到锁的获取和释放。
参考文献
https://tech.meituan.com/2018/11/15/java-lock.html
https://blog.csdn.net/boyeleven/article/details/81390738
https://stackoverflow.com/questions/3362303/whats-a-monitor-in-java
https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html#jvms-6.5.monitorenter
网友评论