日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

深入理解AQS之Semaphore&CountDownLatch&Cyclic詳解

作者:bingtanghulu_6 更新時間: 2022-05-11 編程語言

目錄

1.Semaphore介紹

1.1 限流操作實戰

1.2 Semaphore源碼解析

?2. countDownLatch介紹

2.1 多個線程等待實戰

?2.2 主線程等待

2.3 源碼分析


1.Semaphore介紹

semaphorer(信號量),是一個基于AQS框架實現的工具類,也是操作系統PV操作在java中的實現。通過發放許可來控制線程,只有拿到許可的線程才能執行代碼,常用于限流操作。

PV操作是一種操作系統實現進程互斥與同步的有效方法:P表示通過,V表示釋放。

P操作:S-1=X,如果X>=0線程執行,如果小于0放入等待隊列中。

V操作:S+1=X,如果X>=0線程執行,如果小于0從等待隊列釋放一個等待線程。

構造器(默認非公平鎖,也有公平鎖實現):

public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

常用的方法:

acquire()獲取許可拿到共享鎖

tryacquire()嘗試獲取鎖,獲取到鎖返回true,沒有返回false

release()釋放許可

1.1 限流操作實戰

模擬限流操作

1. 5個線程發送3個許可。

2. 10個核心線程發送5個許可。

import java.util.concurrent.Semaphore;

/**
 * 限流操作
 */
public class SemaphoreTest {

    private static Semaphore semaphore = new Semaphore(3);//發放三個許可

    public static void main(String[] args) throws Exception {

        //執行5個線程
        for(int i=0;i<5;i++){
            new Thread(()->{
                try{
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"開始購票");
                    Thread.sleep(5000);
                    System.out.println(Thread.currentThread().getName()+"購票成功");
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release();
                }
            }).start();

        }

    }
}
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 使用線程池-限流操作
 */
public class SemaphoreTest2 {

    //定義一個5個許可的信號量
    private static Semaphore semaphore = new Semaphore(5);

    //定義一個10個線程的線程池
    private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,20,60,
            TimeUnit.SECONDS,new LinkedBlockingQueue<>(200));

    public static void main(String[] args) throws InterruptedException {

        //每100ms發送一次請求
        for(;;){
            Thread.sleep(100);
            threadPoolExecutor.execute(()->exec());
            System.out.println("-------------------------------------");
        }
    }

    private static void exec(){
        //模擬限流場景
        try{
            semaphore.acquire(1);
            //暫停2秒
            Thread.sleep(2000);
            System.out.println("執行業務邏輯");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            semaphore.release(1);
        }
    }
}

1.2 Semaphore源碼解析

關注點:

1.加解鎖邏輯(共享鎖)實現

2.競爭鎖失敗入隊操作阻塞和喚醒同步隊列中等待線程邏輯

服了這代碼了,寫的狗屁不通

?

?

?2. countDownLatch介紹

?countDownLatch(閉鎖)是一個同步協助類,用于一個或多個線程等待,相當于一個倒計時計數器,需要當計數器為0時才釋放等待線程執行,等待線程會一次性全部返回,計數器不會重置,需要重置需要使用CyclicBarrier

?常用方法:

public void await() 讓線程等待
public boolean await(long timeout, TimeUnit unit) 讓線程等待一段時間,沒有置為0執行業務
countDown() count減1

countDownLatch比join方法更加靈活,join方法底層在不停檢測線程狀態是否存活。

countDownLatch與CyclicBarrier區別:

1.countDownLatch只能使用一次,CyclicBarrier可以通過reset方法重復使用

2.countDownLatch是通過AQS的共享鎖實現的,CyclicBarrier是通過reentrantlock的獨占鎖和condition實現線程喚醒。

2.1 多個線程等待實戰

定義一個計數器為3的閉鎖,線程掛起,等待2秒計數器減1,直到最后一個線程減到0跑線程

import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest {

    //定義一個計數器為3的閉鎖
    private static CountDownLatch countDownLatch = new CountDownLatch(3);

    public static void main(String[] args) throws InterruptedException {
        for (int i =0 ;i<5;i++){
            //阻塞線程
            new Thread(()->{
                try{
                    countDownLatch.await();
                    System.out.println(Thread.currentThread().getName()+"執行業務");
                }catch(Exception e){
                    e.printStackTrace();
                }
            }).start();

            //等待2秒計數器減1
            Thread.sleep(2000);
            countDownLatch.countDown();
        }

    }
}

?2.2 主線程等待


import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest2 {

    public static void main(String[] args) throws InterruptedException {
        //1. 定義一個計數器為3的線程
        CountDownLatch countDownLatch = new CountDownLatch(3);

        //2.讓線程計數器減1
        for(int i=0;i<3;i++){
            new Thread(()->{
                try{
                    Thread.sleep(1000);
                    countDownLatch.countDown();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }).start();
        }

        //3.主線程阻塞,當計數器為0時執行業務
        countDownLatch.await();
        System.out.println("執行業務");
    }
}

2.3 源碼分析

關注點:

加解鎖邏輯

1. countDownLatch.await()加鎖:默認寫死1永遠waitStatus為-1走阻塞線程邏輯

2. countDownLatch.countDown()解鎖:cas操作減1,直到為0執行喚醒線程邏輯?

解決synchronized兩個線程導致的死鎖問題:使用wait方法釋放鎖的形式解決,線上只能kill了

?

原文鏈接:https://blog.csdn.net/qq_21575929/article/details/124658407

欄目分類
最近更新