精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

AQS的同步組件有哪些呢?

OSC開源社區 ? 來源:OSCHINA 社區 ? 2023-03-16 09:42 ? 次閱讀

摘要:AQS 的全稱為 Abstract Queued Synchronizer,是在 J.U.C(java.util.concurrent)下子包中的類。

AQS 的全稱為 Abstract Queued Synchronizer,是在 J.U.C(java.util.concurrent)下子包中的類。

一、AQS 的設計如下

(1)使用 Node 實現 FIFO 隊列,可以用于構建鎖或者其他同步裝置的基礎框架。

(2)利用了一個 int 類型表示狀態

在 AQS 類中,有一個叫做 state 的成員變量。

基于 AQS 有一個同步組件 ReentrantLock,在 ReentrantLock 中,state 表示獲取鎖的線程數。如果 state=0, 則表示還沒有線程獲取鎖;如果 state=1, 則表示有線程獲取了鎖;如果 state>1,則表示重入鎖的數量。

(3)使用方法是繼承

設計上基于模板方法,使用時需要繼承 AQS,并覆寫其中的方法

(4)子類通過繼承并通過實現它的方法管理其狀態 {acquire 和 release} 的方法操縱狀態

(5)可以同時實現排它鎖和共享鎖模式(獨占、共享)

站在使用者的角度,AQS 的功能主要分為兩類:獨占模式和共享模式。它的所有子類中要么實現并使用了它的獨占功能的 API,要么使用了共享鎖的功能,而不會同時使用兩套 API。即便是它最有名的子類 ——ReentrantReadWriteLock,也是通過兩個內部類 ——ReadLock(讀鎖)和 WriteLock(寫鎖)兩套 API 來實現的。

二、AQS 內部實現的大體思路

首先,AQS 內部維護了一個 CLH 隊列來管理鎖,線程會首先嘗試獲取鎖,如果失敗,就將當前線程以及等待等信息封裝成一個 Node 節點,加入到同步隊列 SyncQueue,接著會不斷循環嘗試獲取鎖,獲取鎖的條件是當前節點為 Head 的直接后繼節點才會嘗試獲取鎖,如果失敗,就會阻塞自己,直到自己被喚醒。

而持有鎖的線程釋放鎖的時候,會喚醒隊列中的后繼線程。基于這些基礎的設計和思路,JDK 提供了許多基于 AQS 的子類,比如:CountDownLatch、Semaphore、CyclicBarrier、ReentrantLock、Condition、FutureTask 等

三、AQS 同步組件

CountDownLatch:閉鎖,通過一個計數,來保證線程是否一直阻塞

Semaphore:控制同一時間并發線程的數目

CyclicBarrier:與 CountDownLatch 類似,都能阻塞進程;

ReentrantLock:可重入鎖

Condition: 在使用時需要 ReentrantLock

FutureTask: 對比 Runnable 和 Callable

1.CountDownLatch

同步輔助類,通過它可以阻塞當前線程。也就是說,能夠實現一個線程或者多個線程一直等待,直到其他線程執行的操作完成。使用一個給定的計數器進行初始化,該計數器的操作是原子操作,即同時只能有一個線程操作該計數器。
調用該類 await () 方法的線程會一直阻塞,直到其他線程調用該類的 countDown () 方法,使當前計數器的值變為 0 為止。

每次調用該類的 countDown () 方法,當前計數器的值就會減 1。當計數器的值減為 0 的時候,所有因調用 await () 方法而處于等待狀態的線程就會繼續往下執行。這種操作只能出現一次,因為該類中的計數器不能被重置。如果需要一個可以重置計數次數的版本,可以考慮使用 CyclicBarrier 類。

CountDownLatch 支持給定時間的等待,超過一定的時間不再等待,使用時只需要在 await () 方法中傳入需要等待的時間即可。此時,await () 方法的方法簽名如下:

public boolean await(long timeout, TimeUnit unit)
CountDownLatch 使用場景 在某些業務場景中,程序執行需要等待某個條件完成后才能繼續執行后續的操作。典型的應用為并行計算:當某個處理的運算量很大時,可以將該運算任務拆分成多個子任務,等待所有的子任務都完成之后,父任務再拿到所有子任務的運算結果進行匯總。

調用 ExecutorService 類的 shutdown () 方法,并不會第一時間內把所有線程全部都銷毀掉,而是讓當前已有的線程全部執行完,之后,再把線程池銷毀掉。 示例代碼如下:
package io.binghe.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
public class CountDownLatchExample {
 private static final int threadCount = 200;
 public static void main(String[] args) throws InterruptedException {
 ExecutorService exec = Executors.newCachedThreadPool();
 final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
 for (int i = 0; i < threadCount; i++){
 final int threadNum = i;
 exec.execute(() -> {
 try {
 test(threadNum);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }finally {
 countDownLatch.countDown();
 }
 });
 }
 countDownLatch.await();
        log.info("finish");
 exec.shutdown();
 }
 private static void test(int threadNum) throws InterruptedException {
 Thread.sleep(100);
 log.info("{}", threadNum);
 Thread.sleep(100);
 }
}

支持給定時間等待的示例代碼如下:

package io.binghe.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@Slf4j
public class CountDownLatchExample {
 private static final int threadCount = 200;
 public static void main(String[] args) throws InterruptedException {
 ExecutorService exec = Executors.newCachedThreadPool();
 final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
 for (int i = 0; i < threadCount; i++){
 final int threadNum = i;
 exec.execute(() -> {
 try {
 test(threadNum);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }finally {
 countDownLatch.countDown();
 }
 });
 }
 countDownLatch.await(10, TimeUnit.MICROSECONDS);
        log.info("finish");
 exec.shutdown();
 }
 private static void test(int threadNum) throws InterruptedException {
 Thread.sleep(100);
 log.info("{}", threadNum);
 }
}

2.Semaphore

控制同一時間并發線程的數目。能夠完成對于信號量的控制,可以控制某個資源可被同時訪問的個數。 提供了兩個核心方法 ——acquire () 方法和 release () 方法。acquire () 方法表示獲取一個許可,如果沒有則等待,release () 方法則是在操作完成后釋放對應的許可。

Semaphore 維護了當前訪問的個數,通過提供同步機制來控制同時訪問的個數。Semaphore 可以實現有限大小的鏈表。 Semaphore 使用場景如 Semaphore 常用于僅能提供有限訪問的資源,比如:數據庫連接數 每次獲取并釋放一個許可,示例代碼如下:

package io.binghe.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
@Slf4j
public class SemaphoreExample {
 private static final int threadCount = 200;
 public static void main(String[] args) throws InterruptedException {
 ExecutorService exec = Executors.newCachedThreadPool();
 final Semaphore semaphore = new Semaphore(3);
 for (int i = 0; i < threadCount; i++){
 final int threadNum = i;
 exec.execute(() -> {
 try {
 semaphore.acquire(); //獲取一個許可
 test(threadNum);
 semaphore.release(); //釋放一個許可
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 });
 }
 exec.shutdown();
 }
 private static void test(int threadNum) throws InterruptedException {
 log.info("{}", threadNum);
 Thread.sleep(1000);
 }
}

每次獲取并釋放多個許可,示例代碼如下:

package io.binghe.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
@Slf4j
public class SemaphoreExample {
 private static final int threadCount = 200;
 public static void main(String[] args) throws InterruptedException {
 ExecutorService exec = Executors.newCachedThreadPool();
 final Semaphore semaphore = new Semaphore(3);
 for (int i = 0; i < threadCount; i++){
 final int threadNum = i;
 exec.execute(() -> {
 try {
 semaphore.acquire(3); //獲取多個許可
 test(threadNum);
 semaphore.release(3); //釋放多個許可
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 });
 }
        log.info("finish");
 exec.shutdown();
 }
 private static void test(int threadNum) throws InterruptedException {
 log.info("{}", threadNum);
 Thread.sleep(1000);
 }
}
假設有這樣一個場景,并發太高了,即使使用 Semaphore 進行控制,處理起來也比較棘手。假設系統當前允許的最高并發數是 3,超過 3 后就需要丟棄,使用 Semaphore 也能實現這樣的場景,示例代碼如下:
package io.binghe.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
@Slf4j
public class SemaphoreExample {
 private static final int threadCount = 200;
 public static void main(String[] args) throws InterruptedException {
 ExecutorService exec = Executors.newCachedThreadPool();
 final Semaphore semaphore = new Semaphore(3);
 for (int i = 0; i < threadCount; i++){
 final int threadNum = i;
 exec.execute(() -> {
 try {
 //嘗試獲取一個許可,也可以嘗試獲取多個許可,
 //支持嘗試獲取許可超時設置,超時后不再等待后續線程的執行
 //具體可以參見Semaphore的源碼
 if (semaphore.tryAcquire()) { 
 test(threadNum);
 semaphore.release(); //釋放一個許可
 }
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 });
 }
        log.info("finish");
 exec.shutdown();
 }
 private static void test(int threadNum) throws InterruptedException {
 log.info("{}", threadNum);
 Thread.sleep(1000);
 }
}

3.CyclicBarrier

是一個同步輔助類,允許一組線程相互等待,直到到達某個公共的屏障點,通過它可以完成多個線程之間相互等待,只有當每個線程都準備就緒后,才能各自繼續往下執行后面的操作。

與 CountDownLatch 有相似的地方,都是使用計數器實現,當某個線程調用了 CyclicBarrier 的 await () 方法后,該線程就進入了等待狀態,而且計數器執行加 1 操作,當計數器的值達到了設置的初始值,調用 await () 方法進入等待狀態的線程會被喚醒,繼續執行各自后續的操作。

CyclicBarrier 在釋放等待線程后可以重用,所以,CyclicBarrier 又被稱為循環屏障。

CyclicBarrier 使用場景

可以用于多線程計算數據,最后合并計算結果的場景

CyclicBarrier 與 CountDownLatch 的區別

(1)CountDownLatch 的計數器只能使用一次,而 CyclicBarrier 的計數器可以使用 reset () 方法進行重置,并且可以循環使用

(2)CountDownLatch 主要實現 1 個或 n 個線程需要等待其他線程完成某項操作之后,才能繼續往下執行,描述的是 1 個或 n 個線程等待其他線程的關系。而 CyclicBarrier 主要實現了多個線程之間相互等待,直到所有的線程都滿足了條件之后,才能繼續執行后續的操作,描述的是各個線程內部相互等待的關系。

(3)CyclicBarrier 能夠處理更復雜的場景,如果計算發生錯誤,可以重置計數器讓線程重新執行一次。

CyclicBarrier 中提供了很多有用的方法,比如:可以通過 getNumberWaiting () 方法獲取阻塞的線程數量,通過 isBroken () 方法判斷阻塞的線程是否被中斷。 示例代碼如下:

package io.binghe.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
public class CyclicBarrierExample {
 private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
 public static void main(String[] args) throws Exception {
 ExecutorService executorService = Executors.newCachedThreadPool();
 for (int i = 0; i < 10; i++){
 final int threadNum = i;
 Thread.sleep(1000);
 executorService.execute(() -> {
 try {
 race(threadNum);
 } catch (Exception e) {
 e.printStackTrace();
 }
 });
 }
executorService.shutdown();
 }
 private static void race(int threadNum) throws Exception{
 Thread.sleep(1000);
 log.info("{} is ready", threadNum);
 cyclicBarrier.await();
 log.info("{} continue", threadNum);
 }
}

設置等待超時示例代碼如下:

package io.binghe.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.*;
@Slf4j
public class CyclicBarrierExample {
 private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
 public static void main(String[] args) throws Exception {
 ExecutorService executorService = Executors.newCachedThreadPool();
 for (int i = 0; i < 10; i++){
 final int threadNum = i;
 Thread.sleep(1000);
 executorService.execute(() -> {
 try {
 race(threadNum);
 } catch (Exception e) {
 e.printStackTrace();
 }
 });
 }
 executorService.shutdown();
 }
 private static void race(int threadNum) throws Exception{
 Thread.sleep(1000);
 log.info("{} is ready", threadNum);
 try{
 cyclicBarrier.await(2000, TimeUnit.MILLISECONDS);
 }catch (BrokenBarrierException | TimeoutException e){
 log.warn("BarrierException", e);
 }
 log.info("{} continue", threadNum);
 }
}
在聲明 CyclicBarrier 的時候,還可以指定一個 Runnable,當線程達到屏障的時候,可以優先執行 Runnable 中的方法。 示例代碼如下:
package io.binghe.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
public class CyclicBarrierExample {
 private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> {
 log.info("callback is running");
 });
 public static void main(String[] args) throws Exception {
 ExecutorService executorService = Executors.newCachedThreadPool();
 for (int i = 0; i < 10; i++){
 final int threadNum = i;
 Thread.sleep(1000);
 executorService.execute(() -> {
 try {
 race(threadNum);
 } catch (Exception e) {
 e.printStackTrace();
 }
 });
 }
 executorService.shutdown();
 }
 private static void race(int threadNum) throws Exception{
 Thread.sleep(1000);
 log.info("{} is ready", threadNum);
 cyclicBarrier.await();
 log.info("{} continue", threadNum);
 }
}

4.ReentrantLock 與鎖

Java 中主要分為兩類鎖,一類是 synchronized 修飾的鎖,另外一類就是 J.U.C 中提供的鎖。J.U.C 中提供的核心鎖就是 ReentrantLock。 ReentrantLock(可重入鎖)與 synchronized 區別:

(1)可重入性

二者都是同一個線程進入 1 次,鎖的計數器就自增 1,需要等到鎖的計數器下降為 0 時,才能釋放鎖。

(2)鎖的實現

synchronized 是基于 JVM 實現的,而 ReentrantLock 是 JDK 實現的

(3)性能的區別

synchronized 優化之前性能比 ReentrantLock 差很多,但是自從 synchronized 引入了偏向鎖,輕量級鎖也就是自旋鎖后,性能就差不多了。

(4)功能區別

便利性:synchronized 使用起來比較方便,并且由編譯器保證加鎖和釋放鎖;ReentrantLock 需要手工聲明加鎖和釋放鎖,最好是在 finally 代碼塊中聲明釋放鎖。

鎖的靈活度和細粒度:在這點上 ReentrantLock 會優于 synchronized

ReentrantLock 獨有的功能如下: (1)ReentrantLock 可指定是公平鎖還是非公平鎖。而 synchronized 只能是非公平鎖。所謂的公平鎖就是先等待的線程先獲得鎖。

(2)提供了一個 Condition 類,可以分組喚醒需要喚醒的線程。而 synchronized 只能隨機喚醒一個線程,或者喚醒全部的線程

(3)提供能夠中斷等待鎖的線程的機制,lock.lockInterruptibly ()。ReentrantLock 實現是一種自旋鎖,通過循環調用 CAS 操作來實現加鎖,性能上比較好是因為避免了使線程進入內核態的阻塞狀態。 synchronized 能做的事情 ReentrantLock 都能做,而 ReentrantLock 有些能做的事情,synchronized 不能做。

在性能上,ReentrantLock 不會比 synchronized 差。 synchronized 的優勢:

(1)不用手動釋放鎖,JVM 自動處理,如果出現異常,JVM 也會自動釋放鎖

(2)JVM 用 synchronized 進行管理鎖定請求和釋放時,JVM 在生成線程轉儲時能夠鎖定信息,這些對調試非常有價值,因為它們能標識死鎖或者其他異常行為的來源。而 ReentrantLock 只是普通的類,JVM 不知道具體哪個線程擁有 lock 對象。

(3)synchronized 可以在所有 JVM 版本中工作,ReentrantLock 在某些 1.5 之前版本的 JVM 中可能不支持 ReentrantLock 中的部分方法說明:

boolean tryLock (): 僅在調用時鎖定未被另一個線程保持的情況下才獲取鎖定

boolean tryLock (long, TimeUnit): 如果鎖定在給定的等待時間內沒有被另一個線程保持,且當前線程沒有被中斷,則獲取這個鎖定。

void lockInterruptibly (): 如果當前線程沒有被中斷,就獲取鎖定;如果被中斷,就拋出異常

boolean isLocked (): 查詢此鎖定是否由任意線程保持

boolean isHeldByCurrentThread (): 查詢當前線程是否保持鎖定狀態;

boolean isFair (): 判斷是否是公平鎖

boolean hasQueuedThread (Thread):查詢指定線程是否在等待獲取此鎖定

boolean hasQueuedThreads (): 查詢是否有線程正在等待獲取此鎖定

boolean getHoldCount (): 查詢當前線程保持鎖定的個數

示例代碼如下:

package io.binghe.concurrency.example.lock;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j
public class LockExample {
 //請求總數
 public static int clientTotal = 5000;
 //同時并發執行的線程數
 public static int threadTotal = 200;
 public static int count = 0;
 private static final Lock lock = new ReentrantLock();
 public static void main(String[] args) throws InterruptedException {
 ExecutorService executorService = Executors.newCachedThreadPool();
 final Semaphore semaphore = new Semaphore(threadTotal);
 final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
 for(int i = 0; i < clientTotal; i++){
 executorService.execute(() -> {
 try{
 semaphore.acquire();
 add();
 semaphore.release();
 }catch (Exception e){
 log.error("exception", e);
 }
 countDownLatch.countDown();
 });
 }
 countDownLatch.await();
 executorService.shutdown();
        log.info("count:{}", count);
 }
 private static void add(){
 lock.lock();
 try{
            count ++;
 }finally {
 lock.unlock();
 }
 }
}

5.ReentrantReadWriteLock

在沒有任何讀寫鎖的時候,才可以取得寫鎖。如果一直有讀鎖存在,則無法執行寫鎖,這就會導致寫鎖饑餓。

示例代碼如下:

package io.binghe.concurrency.example.lock;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@Slf4j
public class LockExample {
 private final Map map = new TreeMap<>();
 private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
 private final Lock readLock = lock.readLock();
 private final Lock writeLock = lock.writeLock();
 public Data get(String key){
 readLock.lock();
 try{
 return map.get(key);
 }finally {
 readLock.unlock();
 }
 }
 public Set getAllKeys(){
 readLock.lock();
 try{
 return map.keySet();
 }finally {
 readLock.unlock();
 }
 }
 public Data put(String key, Data value){
 writeLock.lock();
 try{
 return map.put(key, value);
 }finally {
 writeLock.unlock();
 }
 }
 class Data{
 }
}

6.StampedLock

控制鎖三種模式:寫、讀、樂觀讀。 StampedLock 的狀態由版本和模式兩個部分組成,鎖獲取方法返回的是一個數字作為票據,用相應的鎖狀態來表示并控制相關的訪問,數字 0 表示沒有寫鎖被授權訪問。

在讀鎖上分為悲觀鎖和樂觀鎖,樂觀讀就是在讀操作很多,寫操作很少的情況下,可以樂觀的認為寫入和讀取同時發生的幾率很小。

因此,不悲觀的使用完全的讀取鎖定。程序可以查看讀取資料之后,是否遭到寫入進行了變更,再采取后續的措施,這樣的改進可以大幅度提升程序的吞吐量。

總之,在讀線程越來越多的場景下,StampedLock 大幅度提升了程序的吞吐量。 StampedLock 源碼中的案例如下,這里加上了注釋

class Point {
private double x, y;
private final StampedLock sl = new StampedLock();
void move(double deltaX, double deltaY) { // an exclusively locked method
long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp);
}
}
//下面看看樂觀讀鎖案例
double distanceFromOrigin() { // A read-only method
long stamp = sl.tryOptimisticRead(); //獲得一個樂觀讀鎖
double currentX = x, currentY = y; //將兩個字段讀入本地局部變量
if (!sl.validate(stamp)) { //檢查發出樂觀讀鎖后同時是否有其他寫鎖發生?
stamp = sl.readLock(); //如果沒有,我們再次獲得一個讀悲觀鎖
try {
currentX = x; // 將兩個字段讀入本地局部變量
currentY = y; // 將兩個字段讀入本地局部變量
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
//下面是悲觀讀鎖案例
void moveIfAtOrigin(double newX, double newY) { // upgrade
// Could instead start with optimistic, not read mode
long stamp = sl.readLock();
try {
while (x == 0.0 && y == 0.0) { //循環,檢查當前狀態是否符合
long ws = sl.tryConvertToWriteLock(stamp); //將讀鎖轉為寫鎖
if (ws != 0L) { //這是確認轉為寫鎖是否成功
stamp = ws; //如果成功 替換票據
x = newX; //進行狀態改變
y = newY; //進行狀態改變
break;
} else { //如果不能成功轉換為寫鎖
sl.unlockRead(stamp); //我們顯式釋放讀鎖
stamp = sl.writeLock(); //顯式直接進行寫鎖 然后再通過循環再試
}
}
} finally {
sl.unlock(stamp); //釋放讀鎖或寫鎖
}
}
}
示例代碼如下:
package io.binghe.concurrency.example.lock;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.StampedLock;
@Slf4j
public class LockExample {
 //請求總數
 public static int clientTotal = 5000;
 //同時并發執行的線程數
 public static int threadTotal = 200;
 public static int count = 0;
 private static final StampedLock lock = new StampedLock();
 public static void main(String[] args) throws InterruptedException {
 ExecutorService executorService = Executors.newCachedThreadPool();
 final Semaphore semaphore = new Semaphore(threadTotal);
 final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
 for(int i = 0; i < clientTotal; i++){
 executorService.execute(() -> {
 try{
 semaphore.acquire();
 add();
 semaphore.release();
 }catch (Exception e){
 log.error("exception", e);
 }
 countDownLatch.countDown();
 });
 }
 countDownLatch.await();
 executorService.shutdown();
        log.info("count:{}", count);
 }
 private static void add(){
//加鎖時返回一個long類型的票據
 long stamp = lock.writeLock();
 try{
            count ++;
 }finally {
 //釋放鎖的時候帶上加鎖時返回的票據
 lock.unlock(stamp);
 }
 }
}
總結:

(1)當只有少量競爭者時,synchronized 是一個很好的通用鎖實現

(2)競爭者不少,但是線程的增長趨勢是可預估的,此時,ReentrantLock 是一個很好的通用鎖實現

(3)synchronized 不會引發死鎖,其他的鎖使用不當可能會引發死鎖。

7.Condition

Condition 是一個多線程間協調通信的工具類,Condition 除了實現 wait 和 notify 的功能以外,它的好處在于一個 lock 可以創建多個 Condition,可以選擇性的通知 wait 的線程 特點:

(1)Condition 的前提是 Lock,由 AQS 中 newCondition () 方法 創建 Condition 的對象

(2)Condition await 方法表示線程從 AQS 中移除,并釋放線程獲取的鎖,并進入 Condition 等待隊列中等待,等待被 signal

(3)Condition signal 方法表示喚醒對應 Condition 等待隊列中的線程節點,并加入 AQS 中,準備去獲取鎖。

示例代碼如下:

package io.binghe.concurrency.example.lock;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j
public class LockExample {
 public static void main(String[] args) {
 ReentrantLock reentrantLock = new ReentrantLock();
 Condition condition = reentrantLock.newCondition();
 new Thread(() -> {
 try {
 reentrantLock.lock();
 log.info("wait signal"); // 1
 condition.await();
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 log.info("get signal"); // 4
 reentrantLock.unlock();
 }).start();
 new Thread(() -> {
 reentrantLock.lock();
 log.info("get lock"); // 2
 try {
 Thread.sleep(3000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 condition.signalAll();
 log.info("send signal ~ "); // 3
 reentrantLock.unlock();
 }).start();
 }
}






審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • fifo
    +關注

    關注

    3

    文章

    387

    瀏覽量

    43553
  • API
    API
    +關注

    關注

    2

    文章

    1485

    瀏覽量

    61817
  • TCLH
    +關注

    關注

    0

    文章

    2

    瀏覽量

    5946

原文標題:詳解AQS的7個同步組件

文章出處:【微信號:OSC開源社區,微信公眾號:OSC開源社區】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    HMC253AQS24 Gerber Files

    HMC253AQS24 Gerber Files
    發表于 02-19 12:51 ?0次下載
    HMC253<b class='flag-5'>AQS</b>24 Gerber Files

    HMC241AQS16 S-Parameters

    HMC241AQS16 S-Parameters
    發表于 02-19 15:49 ?3次下載
    HMC241<b class='flag-5'>AQS</b>16 S-Parameters

    HMC245AQS16 Gerber Files

    HMC245AQS16 Gerber Files
    發表于 03-11 15:54 ?3次下載
    HMC245<b class='flag-5'>AQS</b>16 Gerber Files

    HMC241AQS16革資料

    HMC241AQS16 Gerber Files
    發表于 03-24 10:12 ?0次下載
    HMC241<b class='flag-5'>AQS</b>16革資料

    HMC253AQS24 S參數

    HMC253AQS24 S參數
    發表于 04-09 14:22 ?2次下載
    HMC253<b class='flag-5'>AQS</b>24 S參數

    HMC241AQS16 S參數

    HMC241AQS16 S參數
    發表于 04-09 14:24 ?1次下載
    HMC241<b class='flag-5'>AQS</b>16 S參數

    HMC253AQS24革資料

    HMC253AQS24革資料
    發表于 05-28 16:51 ?1次下載
    HMC253<b class='flag-5'>AQS</b>24革資料

    HMC253AQS24 S參數

    HMC253AQS24 S參數
    發表于 05-28 17:47 ?1次下載
    HMC253<b class='flag-5'>AQS</b>24 S參數

    HMC241AQS16 S參數

    HMC241AQS16 S參數
    發表于 05-30 11:18 ?0次下載
    HMC241<b class='flag-5'>AQS</b>16 S參數

    HMC245AQS16E S參數

    HMC245AQS16E S參數
    發表于 05-30 20:36 ?1次下載
    HMC245<b class='flag-5'>AQS</b>16E S參數

    HMC245AQS16革資料

    HMC245AQS16革資料
    發表于 06-01 12:30 ?0次下載
    HMC245<b class='flag-5'>AQS</b>16革資料

    HMC241AQS16革資料

    HMC241AQS16革資料
    發表于 06-01 15:13 ?0次下載
    HMC241<b class='flag-5'>AQS</b>16革資料

    基于AQS共享模式的同步計數器——CountDownLatch

    await(): 調用該方法的線程會被掛起,直到 CountDownLatch 計數器的值為 0 才繼續執行,底層使用的是 AQS 的 tryAcquireShared()
    發表于 04-24 15:02 ?672次閱讀
    基于<b class='flag-5'>AQS</b>共享模式的<b class='flag-5'>同步</b>計數器——CountDownLatch

    AQS如何解決線程同步與通信問題

    我們在第一篇中說到AQS使用的是管程模型,而管程模型是使用條件變量來解決同步通信問題的。條件變量會有兩個方法,喚醒和等待。當條件滿足時,我們會通過喚醒方法將條件隊列中的線程放入第二篇所說的同步隊列中
    的頭像 發表于 10-13 11:23 ?476次閱讀

    AQS是什么

    那什么是管程?所謂管程,就是 管理共享變量以及對共享變量操作的過程 ,其三種模型,分別為 Hasen 模型、Hoare 模型和 MESA 模型。目前應用最廣泛的是MESA模型,而JAVA采用
    的頭像 發表于 10-13 14:54 ?480次閱讀
    <b class='flag-5'>AQS</b>是什么