Java併發| CountDownLatch、Semaphore和CyclicBarrier

CountDownLatch

CountDownLatch是一個計數器閉鎖,通過它可以完成類似於阻塞當前線程的功能,即:一個線程或多個線程一直等待,直到其他線程執行的操作完成。當計數器值減至零時,所有因調用await()方法而處於等待狀態的線程就會繼續往下執行。這種現象只會出現一次,計數器不能被重置。

package com.xiaobu.JUC;

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

/**
 * @author xiaobu
 * @version JDK1.8.0_171
 * @date on  2019/9/20 11:27
 * @description 考試場景 20個學生參加考試 一個人老師監考,只有最後一個學生交卷 老師纔算任務完成
 */
public class CountDownLatchDemo {

    private static final int count = 20;

  static   class Teacher implements Runnable{

        private CountDownLatch countDownLatch;

        public Teacher(){}
        public Teacher(CountDownLatch countDownLatch){
            this.countDownLatch = countDownLatch;
        }
        @Override
        public void run() {
            System.out.print("老師髮捲子\n");
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.print("老師收卷子\n");

        }
    }



   static class Student implements Runnable{

        private CountDownLatch countDownLatch;

        private int num;

        public Student(){}
        public Student(CountDownLatch countDownLatch,int num){
            this.countDownLatch = countDownLatch;
            this.num = num;
        }
        @Override
        public void run() {
            System.out.printf("學生(%d)寫卷子\n",num);
            System.out.printf("學生(%d)交卷子\n",num);
            countDownLatch.countDown();
        }
    }


    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(count);
        Teacher teacher = new Teacher(countDownLatch);
        Thread teacherThread = new Thread(teacher);
        teacherThread.start();
        try {
            //爲了防止還沒髮捲子 學生就開始寫卷子了
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < count; i++) {
            Student student = new Student(countDownLatch, i);
            Thread studentThread = new Thread(student);
            studentThread.start();
        }
    }
}

package com.xiaobu.note.JUC.CountDownLatch;

import java.util.concurrent.CountDownLatch;

/**
 * @author xiaobu
 * @version JDK1.8.0_171
 * @date on  2019/2/26 16:34
 * @description V1.0
 */
public class Boss implements Runnable {
    private CountDownLatch downLatch;

    public Boss(CountDownLatch downLatch){
        this.downLatch = downLatch;
    }

    @Override
    public void run() {
        System.out.println("老闆正在等所有的工人幹完活......");
        try {
            this.downLatch.await();
        } catch (InterruptedException e) {
        }
        System.out.println("工人活都幹完了,老闆開始檢查了!");
    }
}

package com.xiaobu.note.JUC.CountDownLatch;

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

/**
 * @author xiaobu
 * @version JDK1.8.0_171
 * @date on  2019/2/26 16:33
 * @description V1.0
 */
public class Worker implements Runnable{

    private CountDownLatch downLatch;
    private String name;

    public Worker(CountDownLatch downLatch, String name){
        this.downLatch = downLatch;
        this.name = name;
    }

    @Override
    public void run() {
        this.doWork();
        try{
            TimeUnit.SECONDS.sleep(new Random().nextInt(10));
        }catch(InterruptedException ie){
           ie.printStackTrace();
        }
        System.out.println(this.name + "活幹完了!");
        this.downLatch.countDown();

    }

    private void doWork(){
        System.out.println(this.name + "正在幹活!");
    }

}


package com.xiaobu.note.JUC.CountDownLatch;

import java.util.concurrent.*;

/**
 * @author xiaobu
 * @version JDK1.8.0_171
 * @date on  2019/2/26 16:34
 * @description V1.0
 */
public class CountDownLatchDemo {
    public static void main(String[] args) {
        //兩者等價但需要顯現的創建線程池這樣不會出現OOM的情況  
        ExecutorService e = Executors.newCachedThreadPool();
        ExecutorService executor=new ThreadPoolExecutor(0,5,60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
        CountDownLatch latch = new CountDownLatch(3);
        Worker w1 = new Worker(latch,"張三");
        Worker w2 = new Worker(latch,"李四");
        Worker w3 = new Worker(latch,"王二");
        Boss boss = new Boss(latch);
        executor.execute(w3);
        executor.execute(w2);
        executor.execute(w1);
        executor.execute(boss);
        executor.shutdown();
    }
}

1589792154(1).jpg

CyclicBarrier

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

package com.xiaobu.JUC;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * @author xiaobu
 * @version JDK1.8.0_171
 * @date on  2019/9/20 11:56
 * @description
 * 最近景色宜人,公司組織去登山,大夥都來到了山腳下,登山過程自由進行。
 *
 * 但爲了在特定的地點拍集體照,規定1個小時後在半山腰集合,誰最後到的,要給大家表演一個節目。
 *
 * 然後繼續登山,在2個小時後,在山頂集合拍照,還是誰最後到的表演節目。
 *
 * 接着開始下山了,在2個小時後在山腳下集合,點名回家,最後到的照例表演節目。
 */
@Slf4j
public class CyclicBarrierDemo {


    private static final int count = 5;

    private static CyclicBarrier cyclicBarrier = new CyclicBarrier(count,new Singer());

    public static void main(String[] args) {
        for (int i = 0; i < count; i++) {
            Staff staff = new Staff(cyclicBarrier, i);
            Thread thread = new Thread(staff);
            thread.start();

        }

    }


    static  class  Singer implements Runnable{

        @Override
        public void run() {
            System.out.println("爲大家表演節目");
        }
    }



    static class  Staff implements Runnable{


        private int num;

        private CyclicBarrier cyclicBarrier;

         public Staff(){}
         public Staff(CyclicBarrier cyclicBarrier,int num){
             this.cyclicBarrier = cyclicBarrier;
             this.num = num;
         }
        @Override
        public void run() {
         log.info("員工[{}]開始出發....",num);
         log.info("員工[{}]到達目的地一....",num);
            try {
                cyclicBarrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
            log.info("員工[{}]再次出發....",num);
            log.info("員工[{}]到達目的地二....",num);
            try {
                cyclicBarrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }

            log.info("員工[{}]又一次出發....",num);
            log.info("員工[{}]到達目的地三....",num);
            try {
                cyclicBarrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
            log.info("員工[{}]結束行程....",num);

        }
    }
}

Semaphore

Semaphore與CountDownLatch相似,不同的地方在於Semaphore的值被獲取到後是可以釋放的,並不像CountDownLatch那樣一直減到底。它也被更多地用來限制流量,類似閥門的 功能。如果限定某些資源最多有N個線程可以訪問,那麼超過N個主不允許再有線程來訪問,同時當現有線程結束後,就會釋放,然後允許新的線程進來。

void acquire() 從信號量獲取一個許可,如果無可用許可前 將一直阻塞等待,

void acquire(int permits) 獲取指定數目的許可,如果無可用許可前 也將會一直阻塞等待

boolean tryAcquire() 從信號量嘗試獲取一個許可,如果無可用許可,直接返回false,不會阻塞

boolean tryAcquire(int permits) 嘗試獲取指定數目的許可,如果無可用許可直接返回false,

boolean tryAcquire(int permits, long timeout, TimeUnit unit) 在指定的時間內嘗試從信號量中獲取許可,如果在指定的時間內獲取成功,返回true,否則返回false

void release() 釋放一個許可,別忘了在finally中使用,注意:多次調用該方法,會使信號量的許可數增加,達到動態擴展的效果,如:初始permits 爲1, 調用了兩次release,最大許可會改變爲2

int availablePermits() 獲取當前信號量可用的許可

package com.xiaobu.JUC;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * @author xiaobu
 * @version JDK1.8.0_171
 * @date on  2019/7/3 11:19
 * @description
 */
@Slf4j
public class SemaphoreDemo2 {
    private final static int threadCount = 5;

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

        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);
                    //把release註釋掉可以看出只執行三個就不能通過了
                     semaphore.release(); // 釋放一個許可
                     System.out.println("semaphore.availablePermits() = " + semaphore.availablePermits());
                } catch (Exception e) {
                    log.error("exception", e);
                }
            });
        }
        exec.shutdown();
    }

    private static void test(int threadNum) throws Exception {
        log.info("{}", threadNum);
        Thread.sleep(2000);
    }
}


SemaphoreDemo2.jpg

release()可以使信號量的許可數增加,達到動態擴展的效果

package com.xiaobu.JUC;

import java.util.concurrent.Semaphore;

/**
 * @author xiaobu
 * @version JDK1.8.0_171
 * @date on  2019/9/27 10:18
 * @description release()可以使信號量的許可數增加,達到動態擴展的效果
 */
public class SemaphoreDemo3 {
  private static   Semaphore semaphore = new Semaphore(1);
    public static void main(String[] args) {
        try {
            System.out.println("獲取前有" + semaphore.availablePermits() + "個可用許可");
            semaphore.acquire();
            System.out.println("獲取後有" + semaphore.availablePermits() + "個可用許可");
            for (int i = 0; i < 3; i++) {
                semaphore.release();
                System.out.println("釋放"+i+"個許可後,還有"+semaphore.availablePermits()+"個可用許可");
            }
            for (int i = 0; i < 3; i++) {
                semaphore.acquire();
                System.out.println("獲取第"+i+"個許可,還有"+semaphore.availablePermits()+"可用許可");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }


}


1571018889(1).jpg

package com.xiaobu.learn.concurrent;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;

import java.util.concurrent.*;

/**
 * @author xiaobu
 * @version JDK1.8.0_171
 * @date on  2019/7/3 11:19
 * @description
 */
@Slf4j
public class SemaphoreDemo {
    private final static int threadCount = 5;

    public static void main(String[] args) throws Exception {
        ThreadFactory threadFactory = new BasicThreadFactory.Builder().namingPattern("thread-pool-%d").daemon(true).build();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(0, 30, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), threadFactory);
        final Semaphore semaphore = new Semaphore(2);

        for (int i = 0; i < threadCount; i++) {
            final int threadNum = i;
            executor.execute(() -> {
                try {
                    if (semaphore.tryAcquire(1,TimeUnit.MILLISECONDS)) { // 嘗試獲取一個許可
                        log.info("[{}]獲取到許可,threadNum:[{}]",Thread.currentThread().getName(),threadNum);
                        test(threadNum);
                    }else {
                        log.info("[{}]沒有獲得許可,threadNum:[{}]",Thread.currentThread().getName(),threadNum);
                    }
                } catch (Exception e) {
                    log.error("exception", e);
                }finally {
                    log.info(Thread.currentThread().getName()+"釋放前semaphore.availablePermits() = {}個" , semaphore.availablePermits());
                    semaphore.release(); // 釋放一個許可
                    log.info(Thread.currentThread().getName()+"釋放後semaphore.availablePermits() ={}個" ,  semaphore.availablePermits());
                }
            });
        }
        //確保所有的線程都執行完
        TimeUnit.SECONDS.sleep(5);
        executor.shutdown();
        log.info("任務結束");
    }

    private static void test(int threadNum) throws Exception {
        System.out.println("test 中的threadNum:"+threadNum);
        Thread.sleep(1000);
    }
}

1569570736(1).jpg

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章