目录
什么是 synchronized ?
synchronized 是 Java 中的一个关键字,主要解决的是多个线程之间访问资源的同步性,可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。
如何使用synchronized
synchronized 关键字的使用方式主要有下面 3 种:
-
修饰实例方法
-
修饰静态方法
-
修饰代码块
修饰实例方法
给当前对象实例加锁,进入代码块要获取对象实例的锁。
public synchronized void method() {
System.out.println("synchronized 方法");
}
修饰静态方法
给当前类加锁,进入代码块要索取当前class的锁。
public synchronized static void method() {
System.out.println("synchronized 静态方法");
}
静态 synchronized 方法和非静态 synchronized 方法之间的调用互斥么?
答案是不会的。因为他们使用的不是同一个锁,非静态 synchronized 方法使用的是当前实例对象锁,而静态 synchronized 方法使用的是当前类的锁。
修饰代码块
对括号里指定的对象/类加锁
synchronized(object)表示进入同步代码库前要获得 给定对象的锁。synchronized(类.class)表示进入同步代码前要获得 给定 Class 的锁。
public void method() {
synchronized (this) {
System.out.println("synchronized 代码块");
}
}
注意:构造方法不能使用 synchronized 关键字修饰。不过,可以在构造方法内部使用synchronized 代码块。
synchronized 的底层原理
synchronized 在修饰同步代码块和修饰方法上有所区别。
synchronized 在修饰同步代码块
使用javap 命令查看类的相关字节码信息。

synchronized 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。
使用两个 monitorexit 指令,这是为了保证锁在同步代码块代码正常执行以及出现异常的这两种情况下都能被正确释放。
当执行 monitorenter 指令时,线程试图获取锁也就是获取 对象监视器 monitor 的持有权。
执行 monitorenter 指令流程:
(1)如果monitor的计数器为0,则意味着该monitor的锁还没有被获得,某个线程获得之后将立即对该计数器加一,然后线程就是这个monitor的所有者了。
(2)如果一个已经拥有该monitor的所有权的线程重入,则会导致monitor计数器再次累加1。
(3)如果monitor已经被其他线程所拥有,则其他线程尝试获取该monitor的所有权时,会被陷入阻塞状态直到monitor计数器变为0,才能再次尝试获取对monitor的所有权。

执行 monitorexit 指令流程:
对象锁的的拥有者线程才可以执行 monitorexit 指令来释放锁。在执行 monitorexit 指令后,将锁计数器减1,直到计数器为0时,表明锁被释放,其他线程可以尝试获取锁。
synchronized 在修饰方法
使用javap 命令查看类的相关字节码信息。

synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,而是 ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法。JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。
如果是实例方法,JVM 会尝试获取实例对象的锁。如果是静态方法,JVM 会尝试获取当前 class 的锁。
总结
synchronized 修饰同步代码块,底层是使用monitorenter和monitorexit指令实现的,monitorenter指令指向同步代码块的开始位置,monitorexit指令则指明同步代码块的结束位置。synchronized 修饰方法,底层使用ACC_SYNCHRONIZED标识,该标识指明了该方法是一个同步方法。
两种情况都需要依赖对象监视器 monitor:
-
Monitor 是
jvm级别的对象,用c++语言实现。线程获得锁需要使用对象(锁)关联monitor。 -
在monitor内部有三个属性,分别是owner、
entrylist、waitset。 -
其中owner是关联的获得锁的线程,并且只能关联一个线程;
entrylist关联的是处于阻塞状态的线程;waitset关联的是处于Waiting状态的线程。
903

被折叠的 条评论
为什么被折叠?



