Java并發(fā) - Lock接口

Locks包 類層次結(jié)構(gòu)

Locks包 類層次結(jié)構(gòu)

Lock接口

方法簽名 描述 說(shuō)明
void lock(); 獲取鎖(不死不休) 一直獲取鎖,直到拿到為止
boolean tryLock(); 獲取鎖(淺嘗輒止) 嘗試獲得鎖刃永,獲取不到就算了
boolean tryLock(long time, TimeUnit unit) throws InterruptedException; 獲取鎖(過(guò)時(shí)不候) 超時(shí)限制货矮,超過(guò)時(shí)間就放棄
void lockInterruptibly() throws InterruptedException; 獲取鎖(任人擺布) 可以在外部通過(guò)方法中斷
void unlock(); 釋放鎖
Condition newCondition();

結(jié)論:
1、lock()最常用;
2斯够、lockInterruptibly()方法一般更昂貴囚玫,有的impl可能沒(méi)有實(shí)現(xiàn)lockInterruptibly(),只有真的需要效應(yīng)中斷時(shí)读规,才使用抓督,使用之前看看impl對(duì)該方法的描述。

  • trylock
package lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class GetLock_Demo {
    static Lock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        lock.lock(); //主線程拿到鎖

        Thread th = new Thread(new Runnable() {
            @Override
            public void run() {
                boolean rs = lock.tryLock();
                System.out.println("是否獲取到鎖: " + rs);
            }
        });
        th.start();

        Thread.sleep(2000L);
        th.interrupt();//中斷線程運(yùn)行
        System.out.println("th 線程被中斷了");
    }
}
是否獲取到鎖: false
th 線程被中斷了
  • trylock帶超時(shí)
package lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class GetLock_Demo {
    static Lock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        lock.lock(); //主線程拿到鎖

        Thread th = new Thread(new Runnable() {
            @Override
            public void run() {
                boolean rs = false;
                try {
                    rs = lock.tryLock(1, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("是否獲取到鎖: " + rs);
            }
        });
        th.start();

        Thread.sleep(2000L);
        th.interrupt();//中斷線程運(yùn)行
        System.out.println("th 線程被中斷了");
    }
}
是否獲取到鎖: false
th 線程被中斷了
  • lockInterruptibly
package lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class GetLock_Demo {
    static Lock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        lock.lock(); //主線程拿到鎖

        Thread th = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock.lockInterruptibly();
                } catch (InterruptedException e) {
                    System.out.println("獲取鎖時(shí)被中斷了");
                    e.printStackTrace();
                }
            }
        });
        th.start();

        Thread.sleep(2000L);
        th.interrupt();//中斷線程運(yùn)行
        System.out.println("th 線程被中斷了");
    }
}
th 線程被中斷了
獲取鎖時(shí)被中斷了
java.lang.InterruptedException
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
    at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
    at lock.GetLock_Demo$1.run(GetLock_Demo.java:16)
    at java.lang.Thread.run(Thread.java:748)
  • lock and unlock
package lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class GetLock_Demo {
    static Lock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        lock.lock(); //主線程拿到鎖

        Thread th = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("嘗試獲得鎖");
                lock.lock();
                System.out.println("獲得鎖了");
            }
        });
        th.start();

        Thread.sleep(2000L);
        th.interrupt();//中斷線程運(yùn)行
        System.out.println("th 線程被中斷了");

        Thread.sleep(5000L);
        lock.unlock();
    }
}
嘗試獲得鎖
th 線程被中斷了
獲得鎖了

Condition

Condition 一般是將其中的await和signal成對(duì)使用的掖桦,且一般是await在前signal在后本昏,而且調(diào)用的使用,應(yīng)該確保本身是獲取到鎖的情況下枪汪,不然會(huì)出現(xiàn)以下問(wèn)題:

1. await 和 signal 方法應(yīng)該在lock內(nèi)部調(diào)用涌穆,否則會(huì)發(fā)生 IllegalMonitorStateException 異常

package lock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Condition_Demo {
    private static Lock lock = new ReentrantLock();
    private static Condition condition = lock.newCondition();

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread() {
            @Override
            public void run() {

                lock.lock();

                try {
                    System.out.println("當(dāng)前線程:" + Thread.currentThread().getName() + "獲得鎖");
                    condition.await(); //因?yàn)檫@里將線程掛起,所以后面無(wú)法執(zhí)行
                    System.out.println("當(dāng)前線程:" + Thread.currentThread().getName() + "開(kāi)始執(zhí)行~");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        };

        thread.start();
        Thread.sleep(2000L);
        System.out.println("休眠2s雀久,來(lái)控制線程");
        condition.signal(); //直接喚醒會(huì)報(bào)錯(cuò)宿稀,因?yàn)閘ock方法執(zhí)行在Thread-0線程內(nèi)部,而我們代碼在這里執(zhí)行的是main線程,所以會(huì)報(bào)錯(cuò),
    }
}
當(dāng)前線程:Thread-0獲得鎖
休眠2s,來(lái)控制線程
Exception in thread "main" java.lang.IllegalMonitorStateException
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.signal(AbstractQueuedSynchronizer.java:1939)
    at lock.Condition_Demo.main(Condition_Demo.java:33)

2. signal應(yīng)該在await后調(diào)用赖捌,否則會(huì)導(dǎo)致死鎖

package lock;

import sync.ReentrantLockDemo;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Condition_Demo {
    private static Lock lock = new ReentrantLock();
    private static Condition condition = lock.newCondition();

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000L);
                    System.out.println("休眠3秒祝沸,等待主線程先執(zhí)行.");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                lock.lock();

                try {
                    System.out.println("當(dāng)前線程:" + Thread.currentThread().getName() + "獲得鎖");
                    condition.await(); //因?yàn)檫@里將線程掛起,所以后面無(wú)法執(zhí)行
                    System.out.println("當(dāng)前線程:" + Thread.currentThread().getName() + "開(kāi)始執(zhí)行~");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        };

        thread.start();
        Thread.sleep(2000L);
        System.out.println("休眠2s越庇,來(lái)控制線程");
        lock.lock();
        condition.signal(); //直接喚醒會(huì)報(bào)錯(cuò)罩锐,因?yàn)閘ock方法執(zhí)行在Thread-0線程內(nèi)部,而我們代碼在這里執(zhí)行的是main線程,所以會(huì)報(bào)錯(cuò),
        lock.unlock(); //獲取到了這把鎖,然后解鎖.
        //2.當(dāng)然這里會(huì)出現(xiàn)死鎖的卤唉,如果signal方法在我們的await之前執(zhí)行涩惑,那么這里就會(huì)死鎖
    }
}
休眠2s,來(lái)控制線程
休眠3秒桑驱,等待主線程先執(zhí)行.
當(dāng)前線程:Thread-0獲得鎖
// 這里死鎖了

  • 使用condition實(shí)現(xiàn)阻塞隊(duì)列的例子
package lock;

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

public class BlockingQueue_Demo {


    public static void main(String[] args) throws InterruptedException {
        BlockingQueue kaneBlockingQueue = new BlockingQueue(6);

        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    kaneBlockingQueue.put("x" + i);
                }
            }
        }.start();

        Thread.sleep(1000L);
        System.out.println("開(kāi)始取元素");
        for (int i = 0; i < 8; i++) {
            kaneBlockingQueue.take();
            Thread.sleep(2000);
        }
    }


}

class BlockingQueue {

    List<Object> list = new ArrayList<>();
    private Lock lock = new ReentrantLock();
    private Condition putCondition = lock.newCondition(); //condition可以有多個(gè)竭恬,針對(duì)不同的操作放入不同condition,相當(dāng)于等待隊(duì)列
    private Condition takeCondition = lock.newCondition();
    private int length;

    public BlockingQueue(int length) {
        this.length = length;
    }

    public void put(Object obj) {
        lock.lock(); //思考一個(gè)讀一個(gè)寫(xiě)莲镣,為什么要加鎖呢遏乔?
        try {
            while (true) {
                if (list.size() < length) { //我們集合的長(zhǎng)度不能超過(guò)規(guī)定的長(zhǎng)度咕村,才能向里面放東西
                    list.add(obj);
                    System.out.println("隊(duì)列中放入元素:" + obj);
                    takeCondition.signal();
                    return;
                } else { //如果放不進(jìn)去速那,就該阻塞. --利用condition實(shí)現(xiàn)
                    putCondition.await();//掛起
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public Object take() {
        lock.lock();
        try {
            while (true) {
                if (list.size() > 0) {
                    Object obj = list.remove(0);
                    System.out.println("隊(duì)列中取得元素:" + obj);
                    putCondition.signal();
                    return obj;
                } else {
                    takeCondition.await();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            return null;
        }
    }
}
隊(duì)列中放入元素:x0
隊(duì)列中放入元素:x1
隊(duì)列中放入元素:x2
隊(duì)列中放入元素:x3
隊(duì)列中放入元素:x4
隊(duì)列中放入元素:x5
開(kāi)始取元素
隊(duì)列中取得元素:x0
隊(duì)列中放入元素:x6
隊(duì)列中取得元素:x1
隊(duì)列中放入元素:x7
隊(duì)列中取得元素:x2
隊(duì)列中放入元素:x8
隊(duì)列中取得元素:x3
隊(duì)列中放入元素:x9
隊(duì)列中取得元素:x4
隊(duì)列中取得元素:x5
隊(duì)列中取得元素:x6
隊(duì)列中取得元素:x7

Process finished with exit code 0


可重入鎖 ReentrantLock

一般來(lái)說(shuō),如果可重入鎖的加鎖次數(shù)是n岔绸,那么解鎖次數(shù)也得是n才能完全釋放鎖理逊,否則,如果小于n 則無(wú)法正常釋放鎖亭螟,此時(shí)如果有別的線程要加鎖挡鞍,則無(wú)法獲取到鎖而被阻塞;如果大于n预烙,則會(huì)觸發(fā) IllegalMonitorStateException 異常, ReentrantLock 默認(rèn)是使用非公平鎖道媚,如果要使用公平鎖扁掸,可以使用 new ReentrantLock(true) 來(lái)創(chuàng)建。

  • 小于n的情況
package lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Reentrant_Demo {
    static Lock lock = new ReentrantLock();

    public static void main(String[] args) {
        lock.lock();
        System.out.println(Thread.currentThread().getName() + "獲得第1次鎖");

        lock.lock();
        System.out.println(Thread.currentThread().getName() + "獲得第2次鎖");

        lock.unlock();

        new Thread() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "開(kāi)始去釋放鎖");
                lock.lock();
                System.out.println("獲得鎖成功~~~");
                lock.unlock();
            }
        }.start();
    }
}
main獲得第1次鎖
main獲得第2次鎖
Thread-0開(kāi)始去釋放鎖
// 子線程獲取鎖失敗導(dǎo)致阻塞了
  • 大于n的情況
    修改成3次unlock
        // 修改成3次unlock
        lock.unlock();
        lock.unlock();
        lock.unlock();
main獲得第1次鎖
main獲得第2次鎖
Exception in thread "main" java.lang.IllegalMonitorStateException
    at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
    at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457)
    at lock.Reentrant_Demo.main(Reentrant_Demo.java:18)
  • 簡(jiǎn)單說(shuō)明圖
    簡(jiǎn)單說(shuō)明圖

實(shí)現(xiàn)一個(gè)ReenrantLock的demo版本 - 一個(gè)現(xiàn)實(shí)思想的簡(jiǎn)單版本

package lock;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;

public class ReentrantLock_Demo implements Lock {
    //記錄鎖的擁有者
    AtomicReference<Thread> owner = new AtomicReference<>();

    //記錄重入次數(shù)的count
    AtomicInteger count = new AtomicInteger(0);

    //等待隊(duì)列
    private LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue();

    @Override
    public boolean tryLock() {
        //判斷count值是否為0最域,如果count不等于0谴分,說(shuō)明鎖被占用
        int ct = count.get();
        //判斷鎖是不是自己占用的,做重入
        if (ct != 0) {
            if (Thread.currentThread() == owner.get()) {
                count.set(ct + 1);
                return true;
            }
        } else { //若count為0 镀脂,表示當(dāng)前鎖未被占用牺蹄,通過(guò)CAS操作
            if (count.compareAndSet(ct, ct + 1)) {
                owner.set(Thread.currentThread());          //如果不是自己,進(jìn)入隊(duì)列
                return true;
            }
        }
        return false;
    }


    @Override
    public void lock() {
        if (!tryLock()) {
            //加入等待隊(duì)列
            waiters.offer(Thread.currentThread());

            while (true) {
                //若線程是隊(duì)列頭部薄翅,先判斷一次沙兰,現(xiàn)在能不能去搶,然后再去加鎖
                Thread head = waiters.peek();
                if (head == Thread.currentThread()) {
                    if (!tryLock()) {
                        LockSupport.park();
                    } else {
                        waiters.poll();
                        return;
                    }
                } else {
                    LockSupport.park();
                }
            }

        }
    }

    public boolean tryUnlock() {
        if (owner.get() != Thread.currentThread()) {
            throw new IllegalMonitorStateException();
        } else {
            int ct = count.get();
            int nextc = ct - 1;
            count.set(nextc);

            if (nextc == 0) { //可重入鎖被加鎖多次翘魄,一旦為0 就釋放鎖鼎天,如果不是0,還得繼續(xù)釋放
                owner.compareAndSet(Thread.currentThread(), null);
                return true;
            } else {
                return false;
            }
        }
    }

    @Override
    public void unlock() {
        if (tryUnlock()) {
            Thread head = waiters.peek();
            if (head != null) {
                LockSupport.unpark(head);
            }
        }
    }

    /**
     * 暫時(shí)忽略
     *
     * @throws InterruptedException
     */
    @Override
    public void lockInterruptibly() throws InterruptedException {

    }


    /**
     * 暫時(shí)忽略
     *
     * @throws InterruptedException
     */

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    /**
     * 暫時(shí)忽略
     *
     * @throws InterruptedException
     */
    @Override
    public Condition newCondition() {
        return null;
    }
}

如果覺(jué)得有收獲就點(diǎn)個(gè)贊吧暑竟,更多知識(shí)斋射,請(qǐng)點(diǎn)擊關(guān)注查看我的主頁(yè)信息哦~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市但荤,隨后出現(xiàn)的幾起案子罗岖,更是在濱河造成了極大的恐慌,老刑警劉巖腹躁,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桑包,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡潜慎,警方通過(guò)查閱死者的電腦和手機(jī)捡多,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門蓖康,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人垒手,你說(shuō)我怎么就攤上這事蒜焊。” “怎么了科贬?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵泳梆,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我榜掌,道長(zhǎng)优妙,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任憎账,我火速辦了婚禮套硼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘胞皱。我一直安慰自己邪意,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布反砌。 她就那樣靜靜地躺著雾鬼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宴树。 梳的紋絲不亂的頭發(fā)上策菜,一...
    開(kāi)封第一講書(shū)人閱讀 51,754評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音酒贬,去河邊找鬼又憨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛同衣,可吹牛的內(nèi)容都是我干的竟块。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼耐齐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼浪秘!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起埠况,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤耸携,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后辕翰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體夺衍,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年喜命,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了沟沙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片河劝。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖矛紫,靈堂內(nèi)的尸體忽然破棺而出赎瞎,到底是詐尸還是另有隱情,我是刑警寧澤颊咬,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布务甥,位于F島的核電站,受9級(jí)特大地震影響喳篇,放射性物質(zhì)發(fā)生泄漏敞临。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一麸澜、第九天 我趴在偏房一處隱蔽的房頂上張望挺尿。 院中可真熱鬧,春花似錦炊邦、人聲如沸票髓。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至以故,卻和暖如春蜗细,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背怒详。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工炉媒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人昆烁。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓吊骤,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親静尼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子白粉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容