Java并发编程——synchronized详解

synchronized 简介

在并发编程中,当多个线程竞争共享资源时,为了实现对共享资源的访问互斥,通常使用 synchronized 关键字进行加锁,在 Java 中,每一个对象都可以作为锁


synchronized 的三种使用方式

修饰代码块

static int count = 0;
static Object lock = new Object();
 
synchronized (lock) {
	count++;
}
 
synchronized (lock) {
	count--;
}

创建 Object 对象 lock 作为锁,对于 count 的自增自减操作,都使用 synchronized 对代码块加锁,当一个线程对 count 进行自增或自减操作时,另外的线程由于获取不到 lock 锁而被阻塞,直到执行完毕,释放锁后,另外的线程才有机会获取锁,保证了临界区内代码的原子性

修饰普通方法

使用 synchronized 修饰普通方法

public class SynchronizedTest {
	public synchronized void test() {

	}
}

等价于

public class SynchronizedTest {
	public void test() {
		synchronized (this) {

		}
	}
}

此时锁是当前调用方法的对象

修饰静态方法

使用 synchronized 修饰静态方法 

public class SynchronizedTest {
	public synchronized static void test() {

	}
}

等价于

public class SynchronizedTest {
	public static void test() {
		synchronized (SynchronizedTest.class) {

		}
	}
}

此时锁是当前类的 class 对象


底层原理详解

Java 对象头

Java 的对象头由 Mark WordKlass Pointer 、Array Length(若为数组对象)组成

Mark Word :保存了对象和锁的信息

Klass Pointer :保存了一个指向类对象的指针

Array Length :保存了数组长度

对于 32 位的 JVM ,Mark Word 的长度是 32 bit ,组成如下

锁状态 25bit 4bit 1bit 2bit
23bit 2bit 是否可偏向 锁标志位
对象的 HashCode 分代年龄 0 01
偏向锁 线程 ID Epoch 分代年龄 1 01
轻量级锁 指向栈中锁记录的指针 00
重量级锁 指向重量级锁的指针 10
GC 标记 11

Monitor

又称监视器或管程,每一个对象都有一个对应的 Monitor

Monitor 结构如图所示

synchronized 原理(重量级锁)

当一个线程通过 synchronized 给一个对象上锁,访问临界区时

static Object lock = new Object();
 
synchronized (lock) {
	count++;
}

该 lock 对象的对象头中的 Mark Word 的前 30 bit 就被设置成指向该对象的 Monitor 的指针,且该 Monitor 的 Owner 设置为该线程,Owner 只能有一个,此时如果有其他线程试图获取锁,则会被阻塞,进入 EntryList 中,当线程执行完毕后释放 Monitor ,并唤醒 EntryList 中阻塞的线程,通过 CPU 调度,让其中一个线程运行

对于 lock 对象的对象头中的 Mark Word 的 HashCode 等信息,会暂存在 Monitor 中,便于解锁后恢复

Guess you like

Origin blog.csdn.net/qq_25274377/article/details/120608989