【Java 并发编程】synchronized

发布时间 2023-10-17 11:38:19作者: LARRY1024

synchronized 关键字

synchronized 是 Java 中的一个关键字,翻译成中文是同步的意思,主要解决的是多个线程之间访问资源的同步性,可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。

使用方法

修饰实例方法

给当前对象实例加锁,进入同步代码前要获得 当前对象实例的锁 。

synchronized void method() {
    ...
}

修饰静态方法

给当前类加锁,会作用于类的所有对象实例 ,进入同步代码前要获得 当前 class 的锁。

synchronized static void method() {
    ...
}

因为静态成员不属于任何一个实例对象,归整个类所有,不依赖于类的特定实例,被类的所有实例共享。

修饰代码块

对括号里指定的对象/类加锁:

  • synchronized(object):表示进入同步代码库前要获得 给定对象 的锁。

  • synchronized(类.class):表示进入同步代码前要获得 给定 Class 的锁

synchronized(this) {
    ...
}

synchronized 底层原理

synchronized 同步语句块

synchronized 同步语句块的实现,使用的是两条指令:monitorenter 指令 和 monitorexit 指令。

在 Java 虚拟机(HotSpot)中,Monitor 是基于 C++ 实现的,由 ObjectMonitor 实现的,每个对象中都内置了一个 ObjectMonitor 对象。

另外,wait/notify 等方法也依赖于 monitor 对象,这就是为什么只有在同步的块或者方法中才能调用 wait/notify 等方法,否则会抛出 java.lang.IllegalMonitorStateException 的异常的原因。

monitorenter 指令

monitorenter 指令:指向同步代码块的开始位置。

当执行 monitorenter 指令时,线程试图获取锁也就是获取 对象监视器 monitor 的持有权,即线程会尝试获取对象的锁,如果锁的计数器为 0,则表示锁可以被获取,获取后将锁计数器加 1。

image

monitorexit 指令

monitorexit 指令:则指明同步代码块的结束位置。

只有对象锁的的拥有者线程才可以执行 monitorexit 指令,来释放锁。在执行 monitorexit 指令后,将锁计数器设为 0,表明锁被释放,其他线程可以尝试获取锁。

image

synchronized 修饰方法

synchronized 修饰的方法会使用 ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法。

JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用:

  • 如果是实例方法,JVM 会尝试获取 实例对象 的锁。

  • 如果是静态方法,JVM 会尝试获取 当前 class 的锁。