天天看點

08ReentrantLock

重入鎖,手動加鎖和釋放鎖,退出臨界區時必須手動釋放,否則其它線程無法通路資源了。

主要有一下幾個特性:

1. 可重入

同一線程可以多次獲得鎖,但是在釋放鎖時,也要釋放相同的次數,如果釋放次數多,會得到一個​

​IllegalMonitorStateException​

​異常,如果釋放次數少,相當于還持有鎖。

public class Test01 {

    public static class LockTest implements Runnable{

        public static ReentrantLock lock = new ReentrantLock();

        public static  int i = 0;

        @Override
        public void run() {
            lock.lock();
            lock.lock();
            try {
                for (int j = 0; j < 10000; j++) {
                    i++;
                }
            }finally {
                lock.unlock();
                lock.unlock();
            }

        }
    }


    @Test
    public void test01() throws Exception{
        LockTest lock1 = new LockTest();
        LockTest lock2 = new LockTest();

        Thread t1 = new Thread(lock1);
        Thread t2 = new Thread(lock2);

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(LockTest.i);
    }
}      

2. 可中斷

可中斷 lockInterruptibly()方法,可重入鎖在等待過程中可以被中斷,而synchronized在等待鎖隻有兩種情況,要麼獲得鎖要麼繼續等待。

首先構造一個死鎖,然後中斷其中一個鎖。

public class Test02 {

    public static class LockTest implements Runnable{

        public static ReentrantLock lock1 = new ReentrantLock();
        public static ReentrantLock lock2 = new ReentrantLock();
        int lock;
        public LockTest(int lock){
            this.lock  = lock;
        }

        @Override
        public void run() {
            try {
                if(lock == 1){
                    lock1.lockInterruptibly();
                    Thread.sleep(500);
                    lock2.lockInterruptibly();
                }else {
                    lock2.lockInterruptibly();
                    Thread.sleep(500);
                    lock1.lockInterruptibly();
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                if(lock1.isHeldByCurrentThread()){
                    lock1.unlock();
                }
                if(lock2.isHeldByCurrentThread()){
                    lock2.unlock();
                }
                System.out.println(Thread.currentThread().getId()+"線程退出");
            }
        }
    }

    @Test
    public void test01() throws Exception{
        LockTest r1 = new LockTest(1);
        LockTest r2 = new LockTest(2);

        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);

        t1.start();
        t2.start();
        Thread.sleep(2000);

        t1.interrupt();
    }

}      

3. 可限時

除了等待外部通知,要避免死鎖的另外一個方法:限時等待。tryLock(),若不加參數運作,鎖被其它線程占用,不會等待,立即傳回

public class Test03 {


    public static class LockTest implements Runnable{
        public static ReentrantLock lock = new ReentrantLock();

        @Override
        public void run() {
            try {
                if(lock.tryLock(3000, TimeUnit.MILLISECONDS)) {
                    Thread.sleep(6000);
                }else {
                    System.out.println("get lock failed");
                }

            }catch (Exception e){
                e.printStackTrace();
            }finally {
                if(lock.isHeldByCurrentThread()){
                    lock.unlock();
                }
            }
        }
    }

    @Test
    public void test01() throws Exception{
        LockTest lockTest = new LockTest();

        Thread t1 = new Thread(lockTest);
        Thread t2 = new Thread(lockTest);

        t1.start();
        t2.start();

        t1.join();
        t2.join();
    }

}      

4. 公平鎖

大多數情況下,鎖申請是不公平的,可以指定new ReentrantLock(true);實作公平鎖獲得鎖時按照請求鎖的順序。

public class Test04 {

    public static class TestLock implements Runnable {
        public static ReentrantLock lock = new ReentrantLock(true);

        @Override
        public void run() {
            while (true) {
                try {
                    lock.lock();
                    System.out.println(Thread.currentThread().getName() + "獲得鎖");
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    @Test
    public void test01() throws Exception{
        TestLock testLock = new TestLock();

        Thread t1 = new Thread(testLock, "th1");
        Thread t2 = new Thread(testLock, "th2");
        Thread t3 = new Thread(testLock, "th3");

        t1.start();
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();

    }

}
      

5. 重要方法整理

​lock()​

​lockInterruptibly()​

​tryLock()​

6. 重入鎖實作主要三個要素:

  1. 原子狀态:原子狀态使用cas操作來儲存目前鎖的狀态,判斷鎖是否已經被别的線程持有。
  2. 等待隊列:所有沒有請求到鎖的線程,會進入等待隊列進行等待。待有線程釋放鎖後,系統就能從等待中喚醒一個線程,繼續工作。
  3. 阻塞原語park()和unpark():用來挂起和恢複線程。沒有得到鎖的線程将會被挂起。

實時内容請關注微信公衆号,公衆号與部落格同時更新:程式員星星