Java 中可重入鎖、不可重入鎖的測試
可重入鎖
指在同一個線程在外層方法獲取鎖的時候,進入內層方法會自動獲取鎖。
為了避免死鎖的發生,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 的時候,導致了死鎖,線程一直等待或者自旋下去。
參考:
http://ifeve.com/java_lock_see4/