摘要:共享鎖能被多個線程同時擁有,能被共享的鎖。需要和聯合使用,它的作用是代替監視器方法,可以通過來休眠喚醒線程。生產者消費產品新建一個線程向倉庫中生產產品。
AQS
AbstractQueuedSynchronizer 抽象類
AQS 是 java 中管理 “鎖” 的抽象類,鎖的許多公共方法都是在這個類中實現。
AQS 是獨占鎖 (例如,ReentrantLock) 和共享鎖 (例如,Semaphore) 的公共父類。
1.1 獨占鎖
鎖在一個時間點只能被一個線程鎖占有。根據鎖的獲取機制,它又劃分為 “ 公平鎖 ” 和 “ 非公平鎖 ”。
1.1.1 公平鎖,是按照通過 CLH 等待線程按照先來先得的規則,公平的獲取鎖;
1.1.2 非公平鎖,則當線程要獲取鎖時,它會無視 CLH 等待隊列而直接獲取鎖。獨占鎖的典型實例子是 ReentrantLock,此外,ReentrantReadWriteLock.WriteLock 也是獨占鎖。
1.2 共享鎖
能被多個線程同時擁有,能被共享的鎖。JUC 包中的 ReentrantReadWriteLock.ReadLock,CyclicBarrier, CountDownLatch 和 Semaphore 都是共享鎖。這些鎖的用途和原理,在以后的章節再詳細介紹。
![圖片上傳中...]
ReentrantLock如果采用Lock,必須主動去釋放鎖;但是發生異常時,不會自動釋放鎖。
因此一般來說,使用Lock必須在try{}catch{}中進行,并且將釋放鎖的操作放在finally塊中進行,以保證鎖一定會被釋放,防止死鎖。
使用Lock 進行同步的話,形式如下:
Lock lock = ...; lock.lock(); // 加鎖 try{ // 處理任務 }catch(Exception ex){ }finally{ lock.unlock(); // 釋放鎖 }
多線程對共享資源進行操作的時候,使用鎖是十分必要的
假設有一個生產者類mPro,一個消費者類mCus,每次操作創建一個線程。
mPro.produce(60); mPro.produce(120); mCus.consume(90); mCus.consume(150); mPro.produce(110);
如果不使用獨占鎖,很可能出現
Thread-0 produce(60) --> size=-60
Thread-4 produce(110) --> size=50
Thread-2 consume(90) <-- size=-60
Thread-1 produce(120) --> size=-60
Thread-3 consume(150) <-- size=-60
因為首先線程1 對size +60, 沒有調用println的時候,切換到線程2, 又加了120, 沒打印又被切換了,然后連續兩次消費了90 和 150再切換到線程1打印,這個時候size就是-60了。
Condition 接口描述了可能會與鎖有關聯的條件變量。Condition 需要和 Lock 聯合使用,它的作用是代替 Object 監視器方法,可以通過 await(),signal() 來休眠 / 喚醒線程。
比如生產者-消費者問題中,共享資源空的時候,消費者不能再消費;共享資源滿的時候,生產者不能再生產;所以Lock 需要借助Condition
private Condition fullCondtion; // 生產條件 private Condition emptyCondtion; // 消費條件
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.Condition; class Depot{ private int capacity; // 倉庫的容量 private int size; // 倉庫的實際數量 private Lock lock; //獨占鎖 private Condition fullCondition; // 生產條件 private Condition emptyCondition; // 消費條件 public Depot(int capacity) { this.capacity = capacity; this.size = 0; this.lock = new ReentrantLock(); this.fullCondition = lock.newCondition(); this.emptyCondition = lock.newCondition(); } public void produce(int val){ lock.lock(); try{ // left 表示“想要生產的數量”(有可能生產量太多,需多此生產) int left = val; while(left>0){ // 庫存已滿時,等待“消費者”消費產品。 while(size>=capacity) { fullCondition.await(); } // 獲取“實際生產的數量”(即庫存中新增的數量) // 如果“庫存”+“想要生產的數量”>“總的容量”,則“實際增量”=“總的容量”-“當前容量”。(此時填滿倉庫) // 否則“實際增量”=“想要生產的數量” int inc = (size+left)>capacity ? (capacity-size) : left; size += inc; left -= inc; System.out.println(Thread.currentThread().getName()+"want to produce" + val + "val-inc"+left + "actually produce "+ inc+ "now size"+size); // 通知“消費者”可以消費了。 emptyCondition.signal(); } } catch(InterruptedException e) { }finally { lock.unlock(); } } public void consume(int val){ lock.lock(); try{ // left 表示“客戶要消費數量”(有可能消費量太大,庫存不夠,需多此消費) int left = val; while (left > 0) { // 庫存為0時,等待“生產者”生產產品。 while (size <= 0) { emptyCondition.await(); } // 獲取“實際消費的數量”(即庫存中實際減少的數量) // 如果“庫存”<“客戶要消費的數量”,則“實際消費量”=“庫存”; // 否則,“實際消費量”=“客戶要消費的數量”。 int dec = (size
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64330.html
摘要:一簡介多線程環境下,我們經常需要多個線程的并發和協作。這個時候,就需要了解一個重要的多線程并發協作模型生產者消費者模式。對于生產者沒有生產產品之前,消費者要進入等待狀態。分析不足在生產者消費者問題中,僅有是不夠的。 一、簡介 多線程環境下,我們經常需要多個線程的并發和協作。 這個時候,就需要了解一個重要的多線程并發協作模型 生產者 / 消費者模式 。 模式簡圖 showImg(h...
摘要:下面是線程相關的熱門面試題,你可以用它來好好準備面試。線程安全問題都是由全局變量及靜態變量引起的。持有自旋鎖的線程在之前應該釋放自旋鎖以便其它線程可以獲得自旋鎖。 最近看到網上流傳著,各種面試經驗及面試題,往往都是一大堆技術題目貼上去,而沒有答案。 不管你是新程序員還是老手,你一定在面試中遇到過有關線程的問題。Java語言一個重要的特點就是內置了對并發的支持,讓Java大受企業和程序員...
摘要:如何使用優化高并發場景寫庫或者耗時計算在的接口中使用消息隊列,把要入庫的數據寫入的類型中。高容錯子進程異常奔潰時,主進程將重建子進程。高性能多進程運行,充分利用多個并行計算,性能強勁。 經常在群里聽到一些朋友問:TP 的項目怎么遷移到 mixphp 來處理高并發,我通常都是回復需要重寫,可是一個開發很久的 TP 項目,代碼量巨大,又怎么可能會花大量時間成本來重寫呢? 那么為何我們不嘗試...
摘要:生產者消費者問題是一個典型的多進程同步問題。生產者線程開始產生新的元素并將它們存儲在緩沖區。否則,生產者線程將會在緩沖區創建一個新元素然后通知消費者。我們建立一個線程池,它將收到兩個任務,生產者和消費者的任務。 原文鏈接:https://dzone.com/articles/th... 作者:Ioan Tinca 譯者:liumapp 想要了解更多關于Java生產者消費者問題的演變嗎?...
閱讀 1640·2023-04-25 20:36
閱讀 2049·2021-09-02 15:11
閱讀 1177·2021-08-27 13:13
閱讀 2653·2019-08-30 15:52
閱讀 4589·2019-08-29 17:13
閱讀 1001·2019-08-29 11:09
閱讀 1491·2019-08-26 11:51
閱讀 833·2019-08-26 10:56