Java同步机制的底层实现("Java 同步机制底层原理详解")
原创
一、Java同步机制的概述
Java同步机制是Java多线程编程中用于控制多个线程对共享资源访问的一种机制。在Java中,synchronized关键字和java.util.concurrent包中的各种同步工具类(如ReentrantLock、Semaphore等)都是同步机制的实现方法。本文将深入探讨Java同步机制的底层实现原理。
二、synchronized关键字
synchronized关键字是Java中实现同步的一种单纯方法。它可以修饰方法或代码块,确保同一时刻只有一个线程可以执行被synchronized修饰的代码。
2.1 同步方法的底层实现
同步方法在底层是通过JVM的锁机制来实现的。当一个线程尝试进入同步方法时,JVM会为该方法生成一个锁(Monitor),然后执行以下步骤:
- 1. 检查锁是否已经被其他线程持有。
- 2. 如果锁未被持有,则当前线程获得锁并执行同步方法。
- 3. 如果锁已被其他线程持有,当前线程将被阻塞,等待锁释放。
- 4. 当持有锁的线程执行完毕,释放锁,其他被阻塞的线程将有机会获得锁并执行。
2.2 同步代码块的底层实现
同步代码块也是通过JVM的锁机制实现的,但与同步方法不同的是,同步代码块可以指定锁对象。以下是同步代码块在底层实现的步骤:
- 1. 线程进入同步代码块前,检查指定锁对象是否已被其他线程持有。
- 2. 如果锁未被持有,当前线程获得锁并执行同步代码块。
- 3. 如果锁已被其他线程持有,当前线程将被阻塞,等待锁释放。
- 4. 当持有锁的线程执行完毕,释放锁,其他被阻塞的线程将有机会获得锁并执行。
三、Java对象头与锁
在Java中,对象头的Mark Word部分用于存储锁状态信息。Mark Word的结构如下:
32位系统:
+-------------------+-------------------+-------------------+-------------------+
| lock state | lock state | lock state | lock state |
+-------------------+-------------------+-------------------+-------------------+
| hash | hash | hash | hash |
+-------------------+-------------------+-------------------+-------------------+
| Java type | Java type | Java type | Java type |
+-------------------+-------------------+-------------------+-------------------+
| array length | array length | array length | array length |
+-------------------+-------------------+-------------------+-------------------+
64位系统:
+-------------------+-------------------+-------------------+-------------------+
| lock state | lock state | lock state | lock state |
+-------------------+-------------------+-------------------+-------------------+
| hash | hash | hash | hash |
+-------------------+-------------------+-------------------+-------------------+
| Java type | Java type | Java type | Java type |
+-------------------+-------------------+-------------------+-------------------+
| array length | array length | array length | array length |
+-------------------+-------------------+-------------------+-------------------+
Mark Word中存储的锁状态信息包括:轻量级锁、重量级锁、偏向锁、自旋锁等。下面分别介绍这些锁的底层实现。
3.1 轻量级锁
轻量级锁是JVM为了缩减锁的开销而引入的一种锁优化技术。当线程尝试获取一个未被其他线程持有的锁时,会使用CAS操作在Mark Word中设置锁状态,并将线程的指针指向当前线程。以下是轻量级锁的底层实现步骤:
1. 当线程尝试获取锁时,使用CAS操作将Mark Word中的锁状态设置为轻量级锁状态,并将线程的指针指向当前线程。
2. 如果CAS操作胜利,当前线程获得锁并执行同步代码。
3. 当持有锁的线程执行完毕,释放锁,将Mark Word中的锁状态恢复为无锁状态。
3.2 重量级锁
当轻量级锁竞争激烈时,JVM会升级为重量级锁。重量级锁的实现依存于操作系统提供的互斥量(Mutex)。以下是重量级锁的底层实现步骤:
1. 当线程尝试获取锁时,首先检查Mark Word中的锁状态。
2. 如果锁状态为无锁或轻量级锁,尝试使用CAS操作将锁状态升级为重量级锁。
3. 如果CAS操作胜利,当前线程将锁对象与操作系统互斥量相关性,并阻塞其他线程。
4. 当持有锁的线程执行完毕,释放锁,将Mark Word中的锁状态恢复为无锁状态,并唤醒其他被阻塞的线程。
3.3 偏向锁
偏向锁是JVM为了缩减无竞争场景下的锁开销而引入的一种锁优化技术。当线程第一次获取锁时,JVM会记录锁的持有者,后续只有该线程尝试获取锁时,才会胜利。以下是偏向锁的底层实现步骤:
1. 当线程尝试获取锁时,检查Mark Word中的锁状态。
2. 如果锁状态为无锁,使用CAS操作将锁状态设置为偏向锁状态,并记录当前线程为锁的持有者。
3. 如果当前线程是锁的持有者,直接获得锁并执行同步代码。
4. 当持有锁的线程执行完毕,释放锁,将Mark Word中的锁状态恢复为无锁状态。
3.4 自旋锁
自旋锁是JVM为了缩减线程在等待锁时的开销而引入的一种锁优化技术。当线程尝试获取锁时,会循环检查锁是否可用,而不是直接阻塞。以下是自旋锁的底层实现步骤:
1. 当线程尝试获取锁时,检查Mark Word中的锁状态。
2. 如果锁状态为无锁,使用CAS操作将锁状态设置为自旋锁状态。
3. 当前线程循环检查锁是否可用,如果锁可用,则获得锁并执行同步代码。
4. 当持有锁的线程执行完毕,释放锁,将Mark Word中的锁状态恢复为无锁状态。
四、Java并发工具类
除了synchronized关键字外,Java还提供了java.util.concurrent包,其中包含了一系列并发工具类,如ReentrantLock、Semaphore、CountDownLatch等。这些工具类在底层也是通过JVM的锁机制实现的,但提供了更多彩的功能。
4.1 ReentrantLock
ReentrantLock是Java中的一种可重入锁,它提供了与synchronized关键字类似的同步功能,但具有更高的灵活性。以下是ReentrantLock的底层实现步骤:
1. 当线程尝试获取锁时,ReentrantLock会使用CAS操作在锁对象中设置锁状态。
2. 如果CAS操作胜利,当前线程获得锁并执行同步代码。
3. 当持有锁的线程执行完毕,释放锁,将锁对象中的锁状态恢复为无锁状态。
4.2 Semaphore
Semaphore是一个计数信号量,它允许一定数量的线程同时访问共享资源。以下是Semaphore的底层实现步骤:
1. 当线程尝试获取信号量时,Semaphore会检查当前可用信号量的数量。
2. 如果信号量数量大于0,使用CAS操作缩减信号量数量,并允许当前线程访问共享资源。
3. 如果信号量数量为0,当前线程将被阻塞,等待其他线程释放信号量。
4. 当持有信号量的线程执行完毕,释放信号量,增多信号量数量,并唤醒其他被阻塞的线程。
4.3 CountDownLatch
CountDownLatch是一个倒计时计数器,它允许一个或多个线程等待其他线程完成操作。以下是CountDownLatch的底层实现步骤:
1. CountDownLatch初始化时,设置一个计数器的初始值。
2. 当线程执行完毕时,使用CAS操作缩减计数器的值。
3. 当计数器的值减为0时,唤醒所有等待的线程。
4. 等待的线程在CountDownLatch上调用await()方法,如果计数器的值不为0,则线程将被阻塞。
五、总结
Java同步机制是Java多线程编程中至关重要的一部分。本文详细介绍了synchronized关键字、Java对象头与锁、以及Java并发工具类的底层实现原理。懂得这些底层原理有助于我们更好地使用Java同步机制,尽大概缩减损耗多线程程序的性能和稳定性。