Java Lock使用

一、lock的使用

  1. 通過源碼可知,Lock類是個(gè)interface,定義了一些方法,其中一個(gè)方法叫l(wèi)ock。ReentrantLock實(shí)現(xiàn)了Lock,我們先簡(jiǎn)單的介紹下lock方法的使用。不同于synchronized,Lock需要我們顯示的調(diào)用unlock才能釋放鎖。先看一段代碼,我們簡(jiǎn)單模擬一個(gè)棧。
private static class CStack{
        Lock mLock = new ReentrantLock();
        SparseArray<Object> array = new SparseArray<>();//效率低,但是節(jié)省內(nèi)存,因?yàn)閷?shí)現(xiàn)方式?jīng)]有采用類似HashMap的Node
        int index = 0;
        public void  push(Object o){
            String Tname = Thread.currentThread().getName();
            try {

                Log.d("Main",Tname+"嘗試獲得鎖");
                mLock.lock();
                Log.d("Main",Tname+"獲得鎖");
                try {
                    Log.d("Main",Tname+"開始push");
                    array.put(index,o);
                    index++;
                    Thread.sleep(1000);
                    Log.d("Main",Tname+"結(jié)束push");

                }catch (Exception e){
                    e.printStackTrace();
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                Log.d("Main",Tname+"釋放鎖");
                mLock.unlock();
            }
        }
    }

我們省略的pop操作,push代碼非常簡(jiǎn)單,邏輯就是push的時(shí)候,要鎖住array,push完成后unlock。push的實(shí)現(xiàn)中,我們故意sleep了1秒,放大操作時(shí)長(zhǎng)。我們簡(jiǎn)單測(cè)試下這段代碼

final CStack dLock = new CStack(); 
for (int i=0;i < 3;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    dLock.push(new Object());
                }
            },i+"").start();
 }

看下執(zhí)行結(jié)果

2020-01-29 22:15:37.198 26285-26329/com.qicloud.dlock D/Main: 0嘗試獲得鎖
2020-01-29 22:15:37.198 26285-26329/com.qicloud.dlock D/Main: 0獲得鎖
2020-01-29 22:15:37.198 26285-26329/com.qicloud.dlock D/Main: 0開始push
2020-01-29 22:15:37.199 26285-26330/com.qicloud.dlock D/Main: 1嘗試獲得鎖
2020-01-29 22:15:37.199 26285-26331/com.qicloud.dlock D/Main: 2嘗試獲得鎖
2020-01-29 22:15:38.199 26285-26329/com.qicloud.dlock D/Main: 0結(jié)束push
2020-01-29 22:15:38.199 26285-26329/com.qicloud.dlock D/Main: 0釋放鎖
2020-01-29 22:15:38.200 26285-26330/com.qicloud.dlock D/Main: 1獲得鎖
2020-01-29 22:15:38.200 26285-26330/com.qicloud.dlock D/Main: 1開始push
2020-01-29 22:15:39.200 26285-26330/com.qicloud.dlock D/Main: 1結(jié)束push
2020-01-29 22:15:39.200 26285-26330/com.qicloud.dlock D/Main: 1釋放鎖
2020-01-29 22:15:39.201 26285-26331/com.qicloud.dlock D/Main: 2獲得鎖
2020-01-29 22:15:39.201 26285-26331/com.qicloud.dlock D/Main: 2開始push
2020-01-29 22:15:40.201 26285-26331/com.qicloud.dlock D/Main: 2結(jié)束push
2020-01-29 22:15:40.202 26285-26331/com.qicloud.dlock D/Main: 2釋放鎖

我們看到,當(dāng)一個(gè)線程拿到鎖后,如果不釋放鎖,那么其他線程會(huì)阻塞在lock處。

二、tryLock的使用

我們把Cstack類稍微改下,代碼如下

    private static class CStack{
        Lock mLock = new ReentrantLock();
        SparseArray<Object> array = new SparseArray<>();//效率低,但是節(jié)省內(nèi)存,因?yàn)閷?shí)現(xiàn)方式?jīng)]有采用類似HashMap的Node
        int index = 0;
        public void  push(Object o){
            String Tname = Thread.currentThread().getName();
            Log.d("Main",Tname+"嘗試獲得鎖");
            if(mLock.tryLock()){
                try {

                    Log.d("Main",Tname+"獲得鎖");
                    try {
                        Log.d("Main",Tname+"開始push");
                        array.put(index,o);
                        index++;
                        Thread.sleep(1000);
                        Log.d("Main",Tname+"結(jié)束push");

                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    Log.d("Main",Tname+"釋放鎖");
                    mLock.unlock();
                }
            }else{
                Log.d("Main",Tname+"獲得鎖失敗");
            }

        }
    }

代碼通過tryLock獲得鎖,通過字面意思我們也能理解這個(gè)函數(shù)是嘗試獲得鎖的意思,我們看下執(zhí)行結(jié)果

2020-01-30 14:24:06.407 16433-16481/com.qicloud.dlock D/Main: 0嘗試獲得鎖
2020-01-30 14:24:06.407 16433-16481/com.qicloud.dlock D/Main: 0獲得鎖
2020-01-30 14:24:06.407 16433-16482/com.qicloud.dlock D/Main: 1嘗試獲得鎖
2020-01-30 14:24:06.407 16433-16481/com.qicloud.dlock D/Main: 0開始push
2020-01-30 14:24:06.407 16433-16482/com.qicloud.dlock D/Main: 1獲得鎖失敗
2020-01-30 14:24:06.407 16433-16483/com.qicloud.dlock D/Main: 2嘗試獲得鎖
2020-01-30 14:24:06.407 16433-16483/com.qicloud.dlock D/Main: 2獲得鎖失敗
2020-01-30 14:24:07.408 16433-16481/com.qicloud.dlock D/Main: 0結(jié)束push
2020-01-30 14:24:07.408 16433-16481/com.qicloud.dlock D/Main: 0釋放鎖

根據(jù)執(zhí)行結(jié)果我們看到tryLock會(huì)立即返回,而不是阻塞。tryLock還提超時(shí)返回的功能,即,在指定的時(shí)間內(nèi)如果沒獲得鎖,那么返回false,在指定時(shí)間內(nèi)是阻塞等待的。

三、lockInterruptibly用法

從字面意思我們也能理解,即,如果線程調(diào)用了interrupt兒被打斷,那么lockInterruptibly會(huì)拋出異常而退出。我們看下代碼

  private static class CStack{
        Lock mLock = new ReentrantLock();
        SparseArray<Object> array = new SparseArray<>();//效率低,但是節(jié)省內(nèi)存,因?yàn)閷?shí)現(xiàn)方式?jīng)]有采用類似HashMap的Node
        int index = 0;
        public void  push(Object o){
            String Tname = Thread.currentThread().getName();

            try {
                Log.d("Main",Tname+"嘗試獲得鎖");
                mLock.lockInterruptibly();
                Log.d("Main",Tname+"獲得鎖");
                Log.d("Main",Tname+"開始push");
                array.put(index,o);
                index++;
                Thread.sleep(2000);
                Log.d("Main",Tname+"結(jié)束push");
                mLock.unlock();

            }catch (InterruptedException e){
               // Log.d("Main",e.getMessage());
                e.printStackTrace();
            }


        }
    }
--------------------------------------------
final CStack dLock = new CStack();
Thread T1 = new Thread(new Runnable() {
     @Override
     public void run() {
            dLock.push(new Object());
     }
},"1");
Thread T2 = new Thread(new Runnable() {
       @Override
        public void run() {
             dLock.push(new Object());
        }
 },"2");
 T1.start();
 T2.start();
 T2.interrupt();

當(dāng)T1獲得鎖的時(shí)候,T2也嘗試獲得鎖,此時(shí)T2會(huì)阻塞,但是這個(gè)阻塞可以被打斷。

四、ReentrantReadWriteLock

通過上面的演示,我們知道ReentranLock是個(gè)互斥鎖,互斥鎖帶來的一個(gè)副作用就是低效,包括synchronized也有這個(gè)問題。ReentrantReadWriteLock類可以實(shí)現(xiàn)一個(gè)非互斥鎖功能。想象這樣一個(gè)場(chǎng)景,很多情況下,多線程讀數(shù)據(jù)是沒問題的??聪麓a

  private static class CStack{
        ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
        SparseArray<Object> array = new SparseArray<>();//效率低,但是節(jié)省內(nèi)存,因?yàn)閷?shí)現(xiàn)方式?jīng)]有采用類似HashMap的Node
        int index = 0;
        public Object getTop(){
            Object t = null;
            String Tname = Thread.currentThread().getName();

            try {
                Log.d("Main",Tname+"嘗試獲得讀鎖");
                mLock.readLock().lock();
                Log.d("Main",Tname+"獲得讀鎖");
                Thread.sleep(3000);
                t = array.get(index);

            }catch (Exception e){

            }finally {
                mLock.readLock().unlock();
                Log.d("Main",Tname+"釋放讀鎖");

            }
            return t;
        }
    }
--------------------------
final CStack dLock = new CStack();
Thread T1 = new Thread(new Runnable() {
       @Override
        public void run() {
            dLock.getTop();
        }
 },"1");
 Thread T2 = new Thread(new Runnable() {
        @Override
        public void run() {
            dLock.getTop();
        }
  },"2");
  T1.start();
  T2.start();

看下執(zhí)行結(jié)果

2020-01-30 19:00:19.282 28478-28518/com.qicloud.dlock D/Main: 1嘗試獲得讀鎖
2020-01-30 19:00:19.282 28478-28518/com.qicloud.dlock D/Main: 1獲得讀鎖
2020-01-30 19:00:19.282 28478-28519/com.qicloud.dlock D/Main: 2嘗試獲得讀鎖
2020-01-30 19:00:19.282 28478-28519/com.qicloud.dlock D/Main: 2獲得讀鎖
2020-01-30 19:00:22.283 28478-28518/com.qicloud.dlock D/Main: 1釋放讀鎖
2020-01-30 19:00:22.283 28478-28519/com.qicloud.dlock D/Main: 2釋放讀鎖

我們看到,ReadLock是一個(gè)非互斥鎖。ReentrantReadWriteLock還提供一個(gè)WriteLock,當(dāng)WriteLock被鎖住的時(shí)候,其他線程是無法獲得ReadLock或者WriteLock的。

五、Condition

Condition是用來實(shí)現(xiàn)線程間協(xié)作的,和 synchronized中wait和notify相差無幾,下面我們來實(shí)現(xiàn)下生產(chǎn)者消費(fèi)者模型??创a

    private static class CStack{
        ReentrantLock mLock = new ReentrantLock();
        Condition produce = mLock.newCondition();
        Condition consume = mLock.newCondition();
        int maxSize = 1;
        List<Object> array = new ArrayList<>();//效率低,但是節(jié)省內(nèi)存,因?yàn)閷?shí)現(xiàn)方式?jīng)]有采用類似HashMap的Node


        public void  push(Object o){
            String Tname = Thread.currentThread().getName();

            try {
                Log.d("Main",Tname+"嘗試獲得鎖");
                mLock.lock();
                Log.d("Main",Tname+"獲得鎖");
                while (array.size() >= maxSize){
                    Log.d("Main",Tname+"Stack已滿,等待消費(fèi)");
                    produce.await();
                }
                Log.d("Main",Tname+"開始push");
                array.add(o);
                Log.d("Main",Tname+"結(jié)束push");
                consume.signal();//通知可消費(fèi)
            }catch (InterruptedException e){
               // Log.d("Main",e.getMessage());
                e.printStackTrace();
            }finally {
                mLock.unlock();
                Log.d("Main",Tname+"釋放鎖");

            }
        }

        public void pop(){
            String Tname = Thread.currentThread().getName();
            try {
                Log.d("Main",Tname+"嘗試獲得鎖");
                mLock.lock();
                Log.d("Main",Tname+"獲得鎖");
                while (array.size() == 0){
                    Log.d("Main",Tname+"Stack為空,等待生產(chǎn)");
                    consume.await();
                }
                Log.d("Main",Tname+"開始pop");
                array.remove(array.size()-1);
                Log.d("Main",Tname+"結(jié)束pop");
                produce.signal();//通知可生產(chǎn)
            }catch (InterruptedException e){
                // Log.d("Main",e.getMessage());
                e.printStackTrace();
            }finally {
                mLock.unlock();
                Log.d("Main",Tname+"釋放鎖");

            }
        }

    }
----------------------
 new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    dLock.push(new Object());
                    try { 
                        Thread.sleep(100);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        },"生產(chǎn)者").start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    dLock.pop();
                    try {
                        Thread.sleep(100);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        },"消費(fèi)者C1").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    dLock.pop();
                    try {
                        Thread.sleep(100);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        },"消費(fèi)者C2").start();

這里的await相當(dāng)于Object的wait,signal相當(dāng)于notify。值得注意的是這段代碼如果用wait/notify實(shí)現(xiàn),極有可能引發(fā)死鎖,但是使用await和signal不會(huì),因?yàn)閟ignal喚醒的是一定能夠處理當(dāng)前情景的線程。

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

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

  • 我們已經(jīng)知道,synchronized 是java的關(guān)鍵字,是Java的內(nèi)置特性,在JVM層面實(shí)現(xiàn)了對(duì)臨界資源的同...
    valor_wang閱讀 446評(píng)論 0 1
  • 線程安全 當(dāng)多個(gè)線程訪問一個(gè)對(duì)象時(shí),如果不用考慮這些線程在運(yùn)行時(shí)環(huán)境下的調(diào)度和交替執(zhí)行,也不需要進(jìn)行額外的同步,或...
    閩越布衣閱讀 806評(píng)論 0 6
  • 線程安全問題 雖然多線程編程極大地提高了效率,但是也會(huì)帶來一定的隱患。舉一個(gè)例子:我們要兩個(gè)線程修改并交替打印變量...
    zhonj閱讀 791評(píng)論 0 1
  • 摘要: 我們已經(jīng)知道 synchronized是java關(guān)鍵字,是java的內(nèi)置特性,在JVM層面實(shí)現(xiàn)了對(duì)臨界資源...
    逗逼程序員閱讀 1,006評(píng)論 0 11
  • Lock和synchronized synchronized都知道是用于同步代碼塊和方法的,線程一旦獲得對(duì)象鎖,其...
    耳_總閱讀 474評(píng)論 0 1

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