JAVA線程協(xié)作:ReentrantLock重入鎖

一、ReentrantLock

我們都知道鎖是為了保護臨界區(qū)資源被多線程并發(fā)訪問時的安全性。 而 ReentrantLock是JAVA提供的完全可以替代Synchronized的方案。在JDK6.0以前Synchronized的執(zhí)行效率遠遠比ReentrantLock的執(zhí)行效率差。而在JDK6.0以后對Synchronized的優(yōu)化后,ReentrantLock與Synchronized的執(zhí)行效率差距不大。

二、ReentrantLock的lock與unlock

  • ReentrantLock的lock與unlock是一套靈活的保護臨界區(qū)資源的方法。我們需要注意如果在使用lock的時候,我們必須要執(zhí)行unlock操作。如果我們只調(diào)用了lock而沒有調(diào)用unlock的釋放鎖的情況下,會造成其他線程獲取不到鎖,而導致阻塞。
public class ReentrantLockDemo implements Runnable{

    public ReentrantLock lock = new ReentrantLock();
    public static int count = 0;

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            try {
                //通過重入鎖,防止并發(fā)增加Count的值
                lock.lock();
                count++;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //使用重入鎖,一定要解鎖,如果沒有解鎖會導致其他線程不可以訪問。
                lock.unlock();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        ReentrantLockDemo reentrantLockDemo = new ReentrantLockDemo();
        Thread t1 = new Thread(reentrantLockDemo);
        Thread t2 = new Thread(reentrantLockDemo);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("Over Count="+count);
    }
}
  • ReentrantLock有一種特性就是ReentrantLock的鎖可以反復的進入,而這種反復的進入僅限于統(tǒng)一線程下,也就是說我們可以將代碼改成下面這種形式。但是我們要記住一點,重復進入鎖調(diào)時,一定要釋放鎖.
public class ReentrantLockDemo implements Runnable{

    public ReentrantLock lock = new ReentrantLock();
    public static int count = 0;

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            try {
                //通過重入鎖,防止并發(fā)增加Count的值
                lock.lock();
                //重復進入鎖
                lock.lock();
                count++;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //使用重入鎖,一定要解鎖,如果沒有解鎖會導致其他線程不可以訪問。
                lock.unlock();
                lock.unlock();
            }
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        ReentrantLockDemo reentrantLockDemo = new ReentrantLockDemo();
        Thread t1 = new Thread(reentrantLockDemo);
        Thread t2 = new Thread(reentrantLockDemo);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("Over Count="+count);
    }
}

二、ReentrantLock的lockInterruptibly

我們知道使用Synchronized的時候,如果鎖被其他線程獲取。而當前線程只能等在其他線程釋放鎖,并且獲得鎖才會執(zhí)行,沒有獲得鎖時處于阻塞狀態(tài)。而ReentrantLock提供了lockInterruptibly來優(yōu)化這種情況。就是通過中斷鎖。例如我們在死鎖的情況下可以通過中斷其中一個線程的鎖來解決這種死鎖的狀態(tài)。

public class IntReentrantLock implements Runnable {

    public ReentrantLock   r1 = new ReentrantLock();
    public ReentrantLock   r2 = new ReentrantLock();
    int lock = 0;
    //控制死鎖
    public IntReentrantLock(int lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        try {
            if(lock == 1){
                //使用lockInterruptibly表明當前的鎖可以被終止.
                r1.lockInterruptibly();
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {

                }
                r2.lockInterruptibly();
            }else{
                r2.lockInterruptibly();
                try {
                    //線程在睡眠的時候被中斷,會拋出InterruptedException異常
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    
                }
                r1.lockInterruptibly();
            }
        }catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if(r1.isHeldByCurrentThread()){
                r1.unlock();
            }
            if(r2.isHeldByCurrentThread()){
                r2.unlock();
            }
            System.out.println("Thread :"+Thread.currentThread().getId()+"-"+Thread.currentThread().getName()+":當前線程退出");
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        IntReentrantLock testLock1 = new IntReentrantLock(1);
        IntReentrantLock testLock2 = new IntReentrantLock(2);
        Thread t1 = new Thread(testLock1,"線程1");
        Thread t2 = new Thread(testLock2,"線程2");
        t1.start();
        t2.start();
        Thread.sleep(1000);
        //中斷線程2
        t2.interrupt();
    }
}
輸出結(jié)果:
Thread :11-線程2:當前線程退出
Thread :10-線程1:當前線程退出

上面的代碼是通過模擬死鎖的狀態(tài)。testLock1調(diào)用的時候需要用到testLock2 的鎖,testLock2 調(diào)用的時候需要用到testLock1的鎖,而雙方有拿著各自的鎖。導致死鎖。我們通過對線程t2進行終端從而解決這種死鎖的狀態(tài)。

三、ReentrantLock的trylock

除了中斷鎖通知的這種操作處理死鎖的這種情況,我們也可以通過設(shè)置獲取鎖的等待時間來進行處理。

public class TryLock implements Runnable {

    public ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        try {
            //等待獲取鎖
            if (lock.tryLock(3, TimeUnit.SECONDS)) {
                System.out.println("get Lock Success");
                Thread.sleep(5000);
            } else {
                System.out.println("get Lock Faild");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (lock.isHeldByCurrentThread()) {
                System.out.println("releas Lock Success");
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TryLock tryLock = new TryLock();
        Thread t1 = new Thread(tryLock);
        Thread t2 = new Thread(tryLock);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }

}

輸出結(jié)果:
get Lock Success
get Lock Faild
releas Lock Success

四、ReentrantLock的condition

我們在使用Synchronized的時候,當某些時候需要在雙線程的協(xié)同處理的時候會使用到了wait()和notify()進行處理這種協(xié)同處理。而ReentrantLock也提供了這種協(xié)同處理就是await()和signal()。

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

    public static Condition condition = lock.newCondition();
     
    @Override
    public void run() {
        try {
            lock.lock();
            condition.await();
            System.out.println("out lock wait");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ReentrantLockCondition reentrantLockCondition= new ReentrantLockCondition();
        Thread t1 = new Thread(reentrantLockCondition);
        t1.start();
        Thread.sleep(2000);
        lock.lock();
        //喚醒t1線程的等待
        condition.signal();
        System.out.println("signal Thread t1");
        //釋放當前的主線程的鎖,如果沒有釋放則喚醒的t1是不可能獲取到鎖,而t1喚醒后獲取不到鎖會處理阻塞狀態(tài)
        lock.unlock();
    }
}

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

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

  • 作者: 一字馬胡 轉(zhuǎn)載標志 【2017-11-03】 更新日志 前言 在java中,鎖是實現(xiàn)并發(fā)的關(guān)鍵組件,多個...
    一字馬胡閱讀 44,319評論 1 32
  • 摘要: 我們已經(jīng)知道,synchronized 是Java的關(guān)鍵字,是Java的內(nèi)置特性,在JVM層面實現(xiàn)了對臨界...
    kingZXY2009閱讀 1,880評論 0 20
  • ?既然java內(nèi)置了synchronized,為什么還要出現(xiàn)lock呢??由于synchronized的并發(fā)是阻塞...
    扈扈哈嘿閱讀 980評論 0 1
  • 愿我快樂,祝自己平安。 愿我高瞻遠矚,看到眼前。 愿我生命長長久久,朝發(fā)夕至。 愿我命中知遇好人,指導良善。 愿我...
    大愛無痕閱讀 262評論 0 0
  • 什么時候我們開始找不到發(fā)自內(nèi)心的笑, 把虛偽變成外衣, 把假面戴在臉上, 壓箱底的校服上, 那充滿豪情壯志的簽名,...
    狂野番茄閱讀 108評論 0 1

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