Java 中可重入鎖、不可重入鎖的測試


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/


 來一道刷了進BAT的面試題?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM