重入鎖ReentrantLock

1. 介紹


  • ReentrantLock顧名思義就是鎖可以重入,ReentrantLock持有一個所計數(shù)器,當已持有所的線程再次獲得該鎖時計數(shù)器值加1,每調(diào)用一次lock.unlock()時所計數(shù)器值減一,直到所計數(shù)器值為0,此時線程釋放鎖。
    例如:一個線程持有鎖,state=1,如果它再次調(diào)用lock方法,那么他將繼續(xù)擁有這把鎖,state=2。當前可重入鎖要完全釋放,調(diào)用了多少次lock方法,還得調(diào)用等量的unlock方法來完全釋放鎖。

2. API使用


  • lock()\unlock() 加鎖與釋放鎖

public class ReentrantLockTest {
    private Lock lock = new ReentrantLock();

    public void testReentrantLock() throws InterruptedException {
        // 線程獲得鎖
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " get lock");
            Thread.sleep(100L);
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " get lock again");
            }finally {
                // 線程釋放鎖
                lock.unlock();
                System.out.println(Thread.currentThread().getName() + " release lock");
            }
        } finally {
            // 線程釋放鎖
            lock.unlock();
            System.out.println(Thread.currentThread().getName() + " release lock again");
        }
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        final ReentrantLockTest test2 = new ReentrantLockTest();
        Thread thread = new Thread(new Runnable(){
            public void run() {
                try {
                    test2.testReentrantLock();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A");
        thread.start();
    }
}
  • 測試鎖與超時

  • lock(), 拿不到lock就不罷休,不然線程就一直block。 比較無賴的做法。
  • tryLock(),馬上返回,拿到lock就返回true,不然返回false。 比較瀟灑的做法。
  • tryLock(long time, TimeUnit unit),帶時間限制拿不到lock,就等一段時間,超時返回false。比較聰明的做法。
  • lockInterruptibly(),線程在sleep或wait,join, 此時如果別的進程調(diào)用此進程的 interrupt()方法,此線程會被喚醒并被要求處理InterruptedException。

代碼示例:

public void lockTest() {
    // 當前線程在鎖可用時直接獲得鎖,鎖不可用時阻塞當前線程  
    lock.lock();
    try {
        System.out.println(Thread.currentThread().getName() + " lock get lock");
        long beginTime = System.currentTimeMillis();
        while (System.currentTimeMillis() - beginTime < 1000) {}
    } finally {
        lock.unlock();
        System.out.println(Thread.currentThread().getName() + " lock release lock");
    }
}

public void tryLockTest() throws InterruptedException {
    long beginTime = System.currentTimeMillis();  
    while(System.currentTimeMillis() - beginTime <100) {}  
    // 當前線程嘗試獲得鎖,如果獲得鎖返回true,否則返回false  
    if(lock.tryLock()) {  
        try{  
            System.out.println(Thread.currentThread().getName() + " tryLock get lock");  
        } finally {  
            lock.unlock();  
            System.out.println(Thread.currentThread().getName() + " tryLock release lock");  
        }  
    } else {  
        System.out.println(Thread.currentThread().getName() + " tryLock can not get lock");  
    }  
}
  • lock方法不能被中斷。如果一個線程在等待獲得一個鎖時被中斷,中斷線程在獲得鎖之前會一直處于 阻塞狀態(tài)。如果出現(xiàn)死鎖,那么lock方法就無法被終止。但是tryLock(long time, TimeUnit unit)和lockInterruptibly方法在申請鎖的過程中是可以被中斷的。
public void tryLockInterruptTest() {  
    long beginTime = System.currentTimeMillis();  
    while(System.currentTimeMillis() - beginTime <100) {}  
    try {  
        //會拋出InterruptedException異常,需要手動處理線程中斷情況。lock.lockInterruptibly()一樣
        if (lock.tryLock(5000, TimeUnit.MILLISECONDS)) {  
            try{  
                System.out.println(Thread.currentThread().getName() + " tryLock get lock");  
            }finally {  
              lock.unlock();  
            }  
        }  
    } catch (InterruptedException e) {  
        // TODO Auto-generated catch block  
        System.out.println(Thread.currentThread().getName() + " was interrupted");  
    }  
 }  
  • 條件對象

  • 通常,線程進入臨界區(qū),卻發(fā)現(xiàn)在某一條件滿足之后才能執(zhí)行,條件對象就是用來管理那些已經(jīng)獲得了鎖,但是卻不能做有用工作的線程。
  • 一個鎖對象可以有一個或多個相關(guān)的條件對象,我們可用lock.newCondition()方法獲得一個條件對象。
  • 與對象的wait()/notify()作用一樣

下面我們直接通過代碼來學習條件對象的使用。
以下代碼實現(xiàn)的是簡單的生產(chǎn)者和消費者案例:

package com.jd.coutdownlatch;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockTest3 {
    private ReentrantLock myLock = new ReentrantLock();

    private Condition condition = myLock.newCondition();

    private List<Integer> listBuffer = new ArrayList<Integer>();

    private volatile boolean runFlag = true;

    /**
     * 生產(chǎn)者 生產(chǎn)數(shù)據(jù)
     */
    public void produce() {
        int i = 0;
        while (runFlag) {
            myLock.lock();
            try {
                // 生產(chǎn)者檢查容器中是否有數(shù)據(jù),如果容器中有數(shù)據(jù)則生產(chǎn)者等待
                // 如果容器中沒有數(shù)據(jù)則生產(chǎn)數(shù)據(jù)放入容器中并通知消費者
                if (listBuffer.size() > 0) {
                    try {
                        // 調(diào)用await()方法,生產(chǎn)者線程阻塞并釋放鎖,之后進入該條件的等待集中
                        // 直到消費者調(diào)用signalAll()方法之后,生產(chǎn)者線程解除阻塞并重新競爭鎖
                        // 生產(chǎn)者線程獲得鎖之后,重新開始從被阻塞的地方繼續(xù)執(zhí)行程序
                        condition.await();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                } else {
                    System.out.println(Thread.currentThread().getName() + " add Integer");
                    listBuffer.add(i++);
                    // 生產(chǎn)者線程調(diào)用signalAll()方法,通知消費者線程容器中有數(shù)據(jù)
                    condition.signalAll();
                }
            } finally {
                myLock.unlock();
            }
        }
    }

    /**
     * 消費者 讀取數(shù)據(jù)
     */
    public void consume() {
        while (runFlag) {
            myLock.lock();
            try {
                // 消費者檢查容器中是否有數(shù)據(jù),如果沒有數(shù)據(jù)消費者等待
                // 如果容器中有數(shù)據(jù)則讀取數(shù)據(jù),讀完之后通知生產(chǎn)者
                if (listBuffer.size() == 0) {
                    try {
                        // 同生產(chǎn)者線程一樣,消費者線程調(diào)用await()方法阻塞并進入該條件等待集中
                        condition.await();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                } else {
                    System.out.println(Thread.currentThread().getName() + " get Integer");
                    long beginTime = 0;
                    System.out.println(listBuffer.remove(0));
                    beginTime = System.currentTimeMillis();
                    while (System.currentTimeMillis() - beginTime < 100) {
                    }
                    // 消費者線程調(diào)用signalAll()方法,通知生產(chǎn)者生產(chǎn)數(shù)據(jù)
                    condition.signalAll();
                }
            } finally {
                myLock.unlock();
            }
        }
    }


    public boolean isRunFlag() {
        return runFlag;
    }

    public void setRunFlag(boolean runFlag) {
        this.runFlag = runFlag;
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        final ReentrantLockTest3 test = new ReentrantLockTest3();

        Thread produce = new Thread(new Runnable() {
            public void run() {
                test.produce();
            }
        }, "A");

        Thread consume = new Thread(new Runnable() {
            public void run() {
                test.consume();
            }
        }, "B");

        produce.start();
        consume.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        test.setRunFlag(false);
    }

}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容