1. 程式人生 > >java高併發系列 - 第16天:JUC中等待多執行緒完成的工具類CountDownLatch,必備技能

java高併發系列 - 第16天:JUC中等待多執行緒完成的工具類CountDownLatch,必備技能

這是java高併發系列第16篇文章。

本篇內容

  1. 介紹CountDownLatch及使用場景
  2. 提供幾個示例介紹CountDownLatch的使用
  3. 手寫一個並行處理任務的工具類

假如有這樣一個需求,當我們需要解析一個Excel裡多個sheet的資料時,可以考慮使用多執行緒,每個執行緒解析一個sheet裡的資料,等到所有的sheet都解析完之後,程式需要統計解析總耗時。分析一下:解析每個sheet耗時可能不一樣,總耗時就是最長耗時的那個操作。

我們能夠想到的最簡單的做法是使用join,程式碼如下:

package com.itsoku.chat13;

import java.util.concurrent.TimeUnit;

/**
 * 微信公眾號:javacode2018,獲取年薪50萬課程
 */
public class Demo1 {

    public static class T extends Thread {
        //休眠時間(秒)
        int sleepSeconds;

        public T(String name, int sleepSeconds) {
            super(name);
            this.sleepSeconds = sleepSeconds;
        }

        @Override
        public void run() {
            Thread ct = Thread.currentThread();
            long startTime = System.currentTimeMillis();
            System.out.println(startTime + "," + ct.getName() + ",開始處理!");
            try {
                //模擬耗時操作,休眠sleepSeconds秒
                TimeUnit.SECONDS.sleep(this.sleepSeconds);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            long endTime = System.currentTimeMillis();
            System.out.println(endTime + "," + ct.getName() + ",處理完畢,耗時:" + (endTime - startTime));
        }
    }

    public static void main(String[] args) throws InterruptedException {
        long starTime = System.currentTimeMillis();
        T t1 = new T("解析sheet1執行緒", 2);
        t1.start();

        T t2 = new T("解析sheet2執行緒", 5);
        t2.start();

        t1.join();
        t2.join();
        long endTime = System.currentTimeMillis();
        System.out.println("總耗時:" + (endTime - starTime));

    }
}

輸出:

1563767560271,解析sheet1執行緒,開始處理!
1563767560272,解析sheet2執行緒,開始處理!
1563767562273,解析sheet1執行緒,處理完畢,耗時:2002
1563767565274,解析sheet2執行緒,處理完畢,耗時:5002
總耗時:5005

程式碼中啟動了2個解析sheet的執行緒,第一個耗時2秒,第二個耗時5秒,最終結果中總耗時:5秒。上面的關鍵技術點是執行緒的join()方法,此方法會讓當前執行緒等待被呼叫的執行緒完成之後才能繼續。可以看一下join的原始碼,內部其實是在synchronized方法中呼叫了執行緒的wait方法,最後被呼叫的執行緒執行完畢之後,由jvm自動呼叫其notifyAll()方法,喚醒所有等待中的執行緒。這個notifyAll()方法是由jvm內部自動呼叫的,jdk原始碼中是看不到的,需要看jvm原始碼,有興趣的同學可以去查一下。所以JDK不推薦線上程上呼叫wait、notify、notifyAll方法。

而在JDK1.5之後的併發包中提供的CountDownLatch也可以實現join的這個功能。

CountDownLatch介紹

CountDownLatch稱之為閉鎖,它可以使一個或一批執行緒在閉鎖上等待,等到其他執行緒執行完相應操作後,閉鎖開啟,這些等待的執行緒才可以繼續執行。確切的說,閉鎖在內部維護了一個倒計數器。通過該計數器的值來決定閉鎖的狀態,從而決定是否允許等待的執行緒繼續執行。

常用方法:

public CountDownLatch(int count):構造方法,count表示計數器的值,不能小於0,否者會報異常。

public void await() throws InterruptedException:呼叫await()會讓當前執行緒等待,直到計數器為0的時候,方法才會返回,此方法會響應執行緒中斷操作。

public boolean await(long timeout, TimeUnit unit) throws InterruptedException:限時等待,在超時之前,計數器變為了0,方法返回true,否者直到超時,返回false,此方法會響應執行緒中斷操作。

public void countDown():讓計數器減1

CountDownLatch使用步驟:

  1. 建立CountDownLatch物件
  2. 呼叫其例項方法await(),讓當前執行緒等待
  3. 呼叫countDown()方法,讓計數器減1
  4. 當計數器變為0的時候,await()方法會返回

示例1:一個簡單的示例

我們使用CountDownLatch來完成上面示例中使用join實現的功能,程式碼如下:

package com.itsoku.chat13;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 微信公眾號:javacode2018,獲取年薪50萬課程
 */
public class Demo2 {

    public static class T extends Thread {
        //休眠時間(秒)
        int sleepSeconds;
        CountDownLatch countDownLatch;

        public T(String name, int sleepSeconds, CountDownLatch countDownLatch) {
            super(name);
            this.sleepSeconds = sleepSeconds;
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            Thread ct = Thread.currentThread();
            long startTime = System.currentTimeMillis();
            System.out.println(startTime + "," + ct.getName() + ",開始處理!");
            try {
                //模擬耗時操作,休眠sleepSeconds秒
                TimeUnit.SECONDS.sleep(this.sleepSeconds);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                countDownLatch.countDown();
            }
            long endTime = System.currentTimeMillis();
            System.out.println(endTime + "," + ct.getName() + ",處理完畢,耗時:" + (endTime - startTime));
        }
    }

    public static void main(String[] args) throws InterruptedException {
        System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + "執行緒 start!");
        CountDownLatch countDownLatch = new CountDownLatch(2);

        long starTime = System.currentTimeMillis();
        T t1 = new T("解析sheet1執行緒", 2, countDownLatch);
        t1.start();

        T t2 = new T("解析sheet2執行緒", 5, countDownLatch);
        t2.start();

        countDownLatch.await();
        System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + "執行緒 end!");
        long endTime = System.currentTimeMillis();
        System.out.println("總耗時:" + (endTime - starTime));

    }
}

輸出:

1563767580511,main執行緒 start!
1563767580513,解析sheet1執行緒,開始處理!
1563767580513,解析sheet2執行緒,開始處理!
1563767582515,解析sheet1執行緒,處理完畢,耗時:2002
1563767585515,解析sheet2執行緒,處理完畢,耗時:5002
1563767585515,main執行緒 end!
總耗時:5003

從結果中看出,效果和join實現的效果一樣,程式碼中建立了計數器為2的CountDownLatch,主執行緒中呼叫countDownLatch.await();會讓主執行緒等待,t1、t2執行緒中模擬執行耗時操作,最終在finally中呼叫了countDownLatch.countDown();,此方法每呼叫一次,CountDownLatch內部計數器會減1,當計數器變為0的時候,主執行緒中的await()會返回,然後繼續執行。注意:上面的countDown()這個是必須要執行的方法,所以放在finally中執行。

示例2:等待指定的時間

還是上面的示例,2個執行緒解析2個sheet,主執行緒等待2個sheet解析完成。主執行緒說,我等待2秒,你們還是無法處理完成,就不等待了,直接返回。如下程式碼:

package com.itsoku.chat13;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 微信公眾號:javacode2018,獲取年薪50萬課程
 */
public class Demo3 {

    public static class T extends Thread {
        //休眠時間(秒)
        int sleepSeconds;
        CountDownLatch countDownLatch;

        public T(String name, int sleepSeconds, CountDownLatch countDownLatch) {
            super(name);
            this.sleepSeconds = sleepSeconds;
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            Thread ct = Thread.currentThread();
            long startTime = System.currentTimeMillis();
            System.out.println(startTime + "," + ct.getName() + ",開始處理!");
            try {
                //模擬耗時操作,休眠sleepSeconds秒
                TimeUnit.SECONDS.sleep(this.sleepSeconds);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                countDownLatch.countDown();
            }
            long endTime = System.currentTimeMillis();
            System.out.println(endTime + "," + ct.getName() + ",處理完畢,耗時:" + (endTime - startTime));
        }
    }

    public static void main(String[] args) throws InterruptedException {
        System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + "執行緒 start!");
        CountDownLatch countDownLatch = new CountDownLatch(2);

        long starTime = System.currentTimeMillis();
        T t1 = new T("解析sheet1執行緒", 2, countDownLatch);
        t1.start();

        T t2 = new T("解析sheet2執行緒", 5, countDownLatch);
        t2.start();

        boolean result = countDownLatch.await(2, TimeUnit.SECONDS);

        System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + "執行緒 end!");
        long endTime = System.currentTimeMillis();
        System.out.println("主執行緒耗時:" + (endTime - starTime) + ",result:" + result);

    }
}

輸出:

1563767637316,main執行緒 start!
1563767637320,解析sheet1執行緒,開始處理!
1563767637320,解析sheet2執行緒,開始處理!
1563767639321,解析sheet1執行緒,處理完畢,耗時:2001
1563767639322,main執行緒 end!
主執行緒耗時:2004,result:false
1563767642322,解析sheet2執行緒,處理完畢,耗時:5002

從輸出結果中可以看出,執行緒2耗時了5秒,主執行緒耗時了2秒,主執行緒中呼叫countDownLatch.await(2, TimeUnit.SECONDS);,表示最多等2秒,不管計數器是否為0,await方法都會返回,若等待時間內,計數器變為0了,立即返回true,否則超時後返回false。

示例3:2個CountDown結合使用的示例

有3個人參見跑步比賽,需要先等指令員發指令槍後才能開跑,所有人都跑完之後,指令員喊一聲,大家跑完了。

示例程式碼:

package com.itsoku.chat13;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 微信公眾號:javacode2018,獲取年薪50萬課程
 */
public class Demo4 {

    public static class T extends Thread {
        //跑步耗時(秒)
        int runCostSeconds;
        CountDownLatch commanderCd;
        CountDownLatch countDown;

        public T(String name, int runCostSeconds, CountDownLatch commanderCd, CountDownLatch countDown) {
            super(name);
            this.runCostSeconds = runCostSeconds;
            this.commanderCd = commanderCd;
            this.countDown = countDown;
        }

        @Override
        public void run() {
            //等待指令員槍響
            try {
                commanderCd.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Thread ct = Thread.currentThread();
            long startTime = System.currentTimeMillis();
            System.out.println(startTime + "," + ct.getName() + ",開始跑!");
            try {
                //模擬耗時操作,休眠runCostSeconds秒
                TimeUnit.SECONDS.sleep(this.runCostSeconds);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                countDown.countDown();
            }
            long endTime = System.currentTimeMillis();
            System.out.println(endTime + "," + ct.getName() + ",跑步結束,耗時:" + (endTime - startTime));
        }
    }

    public static void main(String[] args) throws InterruptedException {
        System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + "執行緒 start!");
        CountDownLatch commanderCd = new CountDownLatch(1);
        CountDownLatch countDownLatch = new CountDownLatch(3);

        long starTime = System.currentTimeMillis();
        T t1 = new T("小張", 2, commanderCd, countDownLatch);
        t1.start();

        T t2 = new T("小李", 5, commanderCd, countDownLatch);
        t2.start();

        T t3 = new T("路人甲", 10, commanderCd, countDownLatch);
        t3.start();

        //主執行緒休眠5秒,模擬指令員準備發槍耗時操作
        TimeUnit.SECONDS.sleep(5);
        System.out.println(System.currentTimeMillis() + ",槍響了,大家開始跑");
        commanderCd.countDown();

        countDownLatch.await();
        long endTime = System.currentTimeMillis();
        System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + "所有人跑完了,主執行緒耗時:" + (endTime - starTime));

    }
}

輸出:

1563767691087,main執行緒 start!
1563767696092,槍響了,大家開始跑
1563767696092,小張,開始跑!
1563767696092,小李,開始跑!
1563767696092,路人甲,開始跑!
1563767698093,小張,跑步結束,耗時:2001
1563767701093,小李,跑步結束,耗時:5001
1563767706093,路人甲,跑步結束,耗時:10001
1563767706093,main所有人跑完了,主執行緒耗時:15004

程式碼中,t1、t2、t3啟動之後,都阻塞在commanderCd.await();,主執行緒模擬發槍準備操作耗時5秒,然後呼叫commanderCd.countDown();模擬發槍操作,此方法被呼叫以後,阻塞在commanderCd.await();的3個執行緒會向下執行。主執行緒呼叫countDownLatch.await();之後進行等待,每個人跑完之後,呼叫countDown.countDown();通知一下countDownLatch讓計數器減1,最後3個人都跑完了,主執行緒從countDownLatch.await();返回繼續向下執行。

手寫一個並行處理任務的工具類

package com.itsoku.chat13;

import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 微信公眾號:javacode2018,獲取年薪50萬課程
 */
public class TaskDisposeUtils {
    //並行執行緒數
    public static final int POOL_SIZE;

    static {
        POOL_SIZE = Integer.max(Runtime.getRuntime().availableProcessors(), 5);
    }

    /**
     * 並行處理,並等待結束
     *
     * @param taskList 任務列表
     * @param consumer 消費者
     * @param <T>
     * @throws InterruptedException
     */
    public static <T> void dispose(List<T> taskList, Consumer<T> consumer) throws InterruptedException {
        dispose(true, POOL_SIZE, taskList, consumer);
    }

    /**
     * 並行處理,並等待結束
     *
     * @param moreThread 是否多執行緒執行
     * @param poolSize   執行緒池大小
     * @param taskList   任務列表
     * @param consumer   消費者
     * @param <T>
     * @throws InterruptedException
     */
    public static <T> void dispose(boolean moreThread, int poolSize, List<T> taskList, Consumer<T> consumer) throws InterruptedException {
        if (CollectionUtils.isEmpty(taskList)) {
            return;
        }
        if (moreThread && poolSize > 1) {
            poolSize = Math.min(poolSize, taskList.size());
            ExecutorService executorService = null;
            try {
                executorService = Executors.newFixedThreadPool(poolSize);
                CountDownLatch countDownLatch = new CountDownLatch(taskList.size());
                for (T item : taskList) {
                    executorService.execute(() -> {
                        try {
                            consumer.accept(item);
                        } finally {
                            countDownLatch.countDown();
                        }
                    });
                }
                countDownLatch.await();
            } finally {
                if (executorService != null) {
                    executorService.shutdown();
                }
            }
        } else {
            for (T item : taskList) {
                consumer.accept(item);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //生成1-10的10個數字,放在list中,相當於10個任務
        List<Integer> list = Stream.iterate(1, a -> a + 1).limit(10).collect(Collectors.toList());
        //啟動多執行緒處理list中的資料,每個任務休眠時間為list中的數值
        TaskDisposeUtils.dispose(list, item -> {
            try {
                long startTime = System.currentTimeMillis();
                TimeUnit.SECONDS.sleep(item);
                long endTime = System.currentTimeMillis();

                System.out.println(System.currentTimeMillis() + ",任務" + item + "執行完畢,耗時:" + (endTime - startTime));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        //上面所有任務處理完畢完畢之後,程式才能繼續
        System.out.println(list + "中的任務都處理完畢!");
    }
}

執行程式碼輸出:

1563769828130,任務1執行完畢,耗時:1000
1563769829130,任務2執行完畢,耗時:2000
1563769830131,任務3執行完畢,耗時:3001
1563769831131,任務4執行完畢,耗時:4001
1563769832131,任務5執行完畢,耗時:5001
1563769833130,任務6執行完畢,耗時:6000
1563769834131,任務7執行完畢,耗時:7001
1563769835131,任務8執行完畢,耗時:8001
1563769837131,任務9執行完畢,耗時:9001
1563769839131,任務10執行完畢,耗時:10001
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]中的任務都處理完畢!

TaskDisposeUtils是一個並行處理的工具類,可以傳入n個任務內部使用執行緒池進行處理,等待所有任務都處理完成之後,方法才會返回。比如我們傳送簡訊,系統中有1萬條簡訊,我們使用上面的工具,每次取100條並行傳送,待100個都處理完畢之後,再取一批按照同樣的邏輯傳送。

java高併發系列

  • java高併發系列 - 第1天:必須知道的幾個概念
  • java高併發系列 - 第2天:併發級別
  • java高併發系列 - 第3天:有關並行的兩個重要定律
  • java高併發系列 - 第4天:JMM相關的一些概念
  • java高併發系列 - 第5天:深入理解程序和執行緒
  • java高併發系列 - 第6天:執行緒的基本操作
  • java高併發系列 - 第7天:volatile與Java記憶體模型
  • java高併發系列 - 第8天:執行緒組
  • java高併發系列 - 第9天:使用者執行緒和守護執行緒
  • java高併發系列 - 第10天:執行緒安全和synchronized關鍵字
  • java高併發系列 - 第11天:執行緒中斷的幾種方式
  • java高併發系列 - 第12天JUC:ReentrantLock重入鎖
  • java高併發系列 - 第13天:JUC中的Condition物件
  • java高併發系列 - 第14天:JUC中的LockSupport工具類,必備技能
  • java高併發系列 - 第15天:JUC中的Semaphore(訊號量)

java高併發系列連載中,總計估計會有四五十篇文章,可以關注公眾號:javacode2018,送年薪50萬課程,獲取最新文章。

相關推薦

java併發系列 - 16JUC等待執行完成工具CountDownLatch必備技能

這是java高併發系列第16篇文章。 本篇內容 介紹CountDownLatch及使用場景 提供幾個示例介紹CountDownLatch的使用 手寫一個並行處理任務的工具類 假如有這樣一個需求,當我們需要解析一個Excel裡多個sheet的資料時,可以考慮使用多執行緒,每個執行緒解析一個sheet裡的資料

java併發系列 - 14JUC的LockSupport工具必備技能

這是java高併發系列第14篇文章。 本文主要內容: 講解3種讓執行緒等待和喚醒的方法,每種方法配合具體的示例 介紹LockSupport主要用法 對比3種方式,瞭解他們之間的區別 LockSupport位於java.util.concurrent(簡稱juc)包中,算是juc中一個基礎類,juc中很多地

java併發系列 - 15JUC的Semaphore最簡單的限流工具必備技能

這是java高併發系列第15篇文章 Semaphore(訊號量)為多執行緒協作提供了更為強大的控制方法,前面的文章中我們學了synchronized和重入鎖ReentrantLock,這2種鎖一次都只能允許一個執行緒訪問一個資源,而訊號量可以控制有多少個執行緒可以同時訪問特定的資源。 Semaphore常用

java併發系列 - 17JUC的迴圈柵欄CyclicBarrier常見的6種使用場景及程式碼示例

這是java高併發系列第17篇。 本文主要內容: 介紹CyclicBarrier 6個示例介紹CyclicBarrier的使用 對比CyclicBarrier和CountDownLatch CyclicBarrier簡介 CyclicBarrier通常稱為迴圈屏障。它和CountDownLatch很相似,

java併發系列 - 23JUC原子一篇就夠了

這是java高併發系列第23篇文章,環境:jdk1.8。 本文主要內容 JUC中的原子類介紹 介紹基本型別原子類 介紹陣列型別原子類 介紹引用型別原子類 介紹物件屬性修改相關原子類 預備知識 JUC中的原子類都是都是依靠volatile、CAS、Unsafe類配合來實現的,需要了解的請移步: volati

java併發系列 - 21java的CAS操作java併發的基石

這是java高併發系列第21篇文章。 本文主要內容 從網站計數器實現中一步步引出CAS操作 介紹java中的CAS及CAS可能存在的問題 悲觀鎖和樂觀鎖的一些介紹及資料庫樂觀鎖的一個常見示例 使用java中的原子操作實現網站計數器功能 我們需要解決的問題 需求:我們開發了一個網站,需要對訪問量進行統計,使

java併發系列 - 22java底層工具Unsafe高手必須要了解

這是java高併發系列第22篇文章,文章基於jdk1.8環境。 本文主要內容 基本介紹 通過反射獲取Unsafe例項 Unsafe中的CAS操作 Unsafe中原子操作相關方法介紹 Unsafe中執行緒排程相關方法 park和unpark示例 Unsafe鎖示例 Unsafe中保證變數的可見性 Unsafe

java併發系列 - 24ThreadLocal、InheritableThreadLocal(通俗易懂)

java高併發系列第24篇文章。 環境:jdk1.8。 本文內容 需要解決的問題 介紹ThreadLocal 介紹InheritableThreadLocal 需要解決的問題 我們還是以解決問題的方式來引出ThreadLocal、InheritableThreadLocal,這樣印象會深刻一些。 目前

java併發系列 - 25掌握JUC的阻塞佇列

這是java高併發系列第25篇文章。 環境:jdk1.8。 本文內容 掌握Queue、BlockingQueue介面中常用的方法 介紹6中阻塞佇列,及相關場景示例 重點掌握4種常用的阻塞佇列 Queue介面 佇列是一種先進先出(FIFO)的資料結構,java中用Queue介面來表示佇列。 Queue介面中

java併發系列 - 27實戰篇介面效能成倍提升讓同事刮目相看現學現用

這是java高併發系列第27篇文章。 開發環境:jdk1.8。 案例講解 電商app都有用過吧,商品詳情頁,需要給他們提供一個介面獲取商品相關資訊: 商品基本資訊(名稱、價格、庫存、會員價格等) 商品圖片列表 商品描述資訊(描述資訊一般是由富文字編輯的大文字資訊) 資料庫中我們用了3張表儲存上面的資訊:

java併發系列 - 31獲取執行執行結果這6種方法你都知道?

這是java高併發系列第31篇。 環境:jdk1.8。 java高併發系列已經學了不少東西了,本篇文章,我們用前面學的知識來實現一個需求: 在一個執行緒中需要獲取其他執行緒的執行結果,能想到幾種方式?各有什麼優缺點? 結合這個需求,我們使用6種方式,來對之前學過的知識點做一個回顧,加深記憶。 方式1:Thre

java併發系列 - 32併發計數器的實現方式有哪些?

這是java高併發系列第32篇文章。 java環境:jdk1.8。 本文主要內容 4種方式實現計數器功能,對比其效能 介紹LongAdder 介紹LongAccumulator 需求:一個jvm中實現一個計數器功能,需保證多執行緒情況下資料正確性。 我們來模擬50個執行緒,每個執行緒對計數器遞增100萬次

java併發系列-1:必須知道的幾個概念

java高併發系列-第1天:必須知道的幾個概念 同步(Synchronous)和非同步(Asynchronous) 同步和非同步通常來形容一次方法呼叫,同步方法呼叫一旦開始,呼叫者必須等到方法呼叫返回後,才能繼續後續的行為。非同步方法呼叫更像一個訊息傳遞,一旦開始,方法呼叫就會立即返回,呼叫者就可以繼續後續的

java併發系列 - 6:執行的基本操作

新建執行緒 新建執行緒很簡單。只需要使用new關鍵字建立一個執行緒物件,然後呼叫它的start()啟動執行緒即可。 Thread thread1 = new Thread1(); t1.start(); 那麼執行緒start()之後,會幹什麼呢?執行緒有個run()方法,start()會建立一個新的執行緒並讓

java併發系列 - 12JUC:ReentrantLock重入鎖

java高併發系列 - 第12天JUC:ReentrantLock重入鎖 本篇文章開始將juc中常用的一些類,估計會有十來篇。 synchronized的侷限性 synchronized是java內建的關鍵字,它提供了一種獨佔的加鎖方式。synchronized的獲取和釋放鎖由jvm實現,使用者不需要顯示的釋

java併發系列 - 29併發常見的限流方式

這是java高併發系列第29篇。 環境:jdk1.8。 本文內容 介紹常見的限流演算法 通過控制最大併發數來進行限流 通過漏桶演算法來進行限流 通過令牌桶演算法來進行限流 限流工具類RateLimiter 常見的限流的場景 秒殺活動,數量有限,訪問量巨大,為了防止系統宕機,需要做限流處理 國慶期間,一般

Java併發包原始碼學習系列掛起與喚醒執行LockSupport工具

[toc] 系列傳送門: - [Java併發包原始碼學習系列:AbstractQueuedSynchronizer](https://blog.csdn.net/Sky_QiaoBa_Sum/article/details/112254373) - [Java併發包原始碼學習系列:CLH同步佇列及同步資源

一步步動手實現併發的Reactor模型 —— Kafka底層如何充分利用執行優勢去處理網路I/O與業務分發

一、從《Apeche Kafka原始碼剖析》上搬來的概念和圖 Kafka網路採用的是Reactor模式,是一種基於事件驅動的模式。熟悉Java程式設計的讀者應該瞭解Java NIO提供了Reactor模式的API。常見的單執行緒Java NIO程式設計模式如圖所示。 熟悉NIO程式設計都應該知道這個Sele

併發等待執行完成CountDownLatch(倒數計時器)。

CountDownLatch允許一個或多個執行緒等待其他執行緒完成操作。 假如有這樣一個需求:我們需要解析一個Excel裡多個sheet的資料,此時可以考慮使用多執行緒,每個執行緒解析一個sheet裡的資料,等到所有的sheet都解析完之後,程式需要提示解析完成。在這個需求中,要實現主執行緒等待

Java執行-併發工具(二)等待執行完成CountDownLatch

參考:https://www.jianshu.com/p/1716ce690637http://ifeve.com/talk-concurrency-countdownlatch/CountDownLatch是什麼CountDownLatch也叫閉鎖,在JDK1.5被引入,允