可重入鎖
指在同一個線程在外層方法獲取鎖的時候,進入內層方法會自動獲取鎖。
為了避免死鎖的發生,JDK 中基本都是可重入鎖。
下面我們來測試一下 synchronized 和 java.util.concurrent.lock.ReentrantLock 鎖的可重入性
- 測試 synchronized 加鎖 可重入性
package constxiong.concurrency.a019;
/**
* 測試 synchronized 加鎖 可重入性
* @author ConstXiong
* @date 2019-09-20 15:55:27
*/
public class TestSynchronizedReentrant {
public static void main(String[] args) {
new Thread(new SynchronizedReentrant()).start();
}
}
class SynchronizedReentrant implements Runnable {
private final Object obj = new Object();
/**
* 方法1,調用方法2
*/
public void method1() {
synchronized (obj) {
System.out.println(Thread.currentThread().getName() + " method1()");
method2();
}
}
/**
* 方法2,打印前獲取 obj 鎖
* 如果同一線程,鎖不可重入的話,method2 需要等待 method1 釋放 obj 鎖
*/
public void method2() {
synchronized (obj) {
System.out.println(Thread.currentThread().getName() + " method2()");
}
}
@Override
public void run() {
//線程啟動 執行方法1
method1();
}
}
打印結果:
Thread-0 method1()
Thread-0 method2()
- 測試 ReentrantLock 的可重入性
package constxiong.concurrency.a019;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 測試 ReentrantLock 的可重入性
* @author ConstXiong
* @date 2019-09-20 16:24:52
*/
public class TestLockReentrant {
public static void main(String[] args) {
new Thread(new LockReentrant()).start();
}
}
class LockReentrant implements Runnable {
private final Lock lock = new ReentrantLock();
/**
* 方法1,調用方法2
*/
public void method1() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " method1()");
method2();
} finally {
lock.unlock();
}
}
/**
* 方法2,打印前獲取 obj 鎖
* 如果同一線程,鎖不可重入的話,method2 需要等待 method1 釋放 obj 鎖
*/
public void method2() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " method2()");
} finally {
lock.unlock();
}
}
@Override
public void run() {
//線程啟動 執行方法1
method1();
}
}
打印結果:
Thread-0 method1()
Thread-0 method2()
測試不可重入鎖
我在 JDK 中沒找到可重入鎖,所以考慮自己實現一下。兩種方式:通過 synchronized wait notify 實現;通過 CAS + 自旋方式實現
1) synchronized wait notify 方式實現
package constxiong.concurrency.a019;
/**
* 不可重入鎖,通過 synchronized wait notify 實現
* @author ConstXiong
* @date 2019-09-20 16:53:34
*/
public class NonReentrantLockByWait {
//是否被鎖
private volatile boolean locked = false;
//加鎖
public synchronized void lock() {
//當某個線程獲取鎖成功,其他線程進入等待狀態
while (locked) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//加鎖成功,locked 設置為 true
locked = true;
}
//釋放鎖
public synchronized void unlock() {
locked = false;
notify();
}
}
2) 通過 CAS + 自旋 方式實現
package constxiong.concurrency.a019;
import java.util.concurrent.atomic.AtomicReference;
/**
* 不可重入鎖,通過 CAS + 自旋 實現
* @author ConstXiong
* @date 2019-09-20 16:53:34
*/
public class NonReentrantLockByCAS {
private AtomicReference<Thread> lockedThread = new AtomicReference<Thread>();
public void lock() {
Thread t = Thread.currentThread();
//當 lockedThread 持有引用變量為 null 時,設置 lockedThread 持有引用為 當前線程變量
while (!lockedThread.compareAndSet(null, t)) {
//自旋,空循環,等到鎖被釋放
}
}
public void unlock() {
//如果是本線程鎖定的,可以成功釋放鎖
lockedThread.compareAndSet(Thread.currentThread(), null);
}
}
測試類
package constxiong.concurrency.a019;
/**
* 測試不可重入鎖
* @author ConstXiong
* @date 2019-09-20 18:08:55
*/
public class TestLockNonReentrant{
public static void main(String[] args) {
new Thread(new LockNonReentrant()).start();
}
}
class LockNonReentrant implements Runnable {
// private final NonReentrantLockByWait lock = new NonReentrantLockByWait();
private final NonReentrantLockByCAS lock = new NonReentrantLockByCAS();
/**
* 方法1,調用方法2
*/
public void method1() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " method1()");
method2();
} finally {
lock.unlock();
}
}
/**
* 方法2,打印前獲取 obj 鎖
* 如果同一線程,鎖不可重入的話,method2 需要等待 method1 釋放 obj 鎖
*/
public void method2() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " method2()");
} finally {
lock.unlock();
}
}
@Override
public void run() {
//線程啟動 執行方法1
method1();
}
}
測試結果,都是在 method1,調用 method2 的時候,導致了死鎖,線程一直等待或者自旋下去。
參考:
- Java 自學指南
- Java 面試題匯總PC端瀏覽【點這里】
- Java知識圖譜
- Java 面試題匯總小程序瀏覽,掃二維碼
所有資源資源匯總於公眾號