代码改变世界

Java并发之CountDownLatch、Semaphore和CyclicBarrier

2019-08-27 20:00  jackyu888  阅读(38)  评论(0)    收藏  举报

JAVA并发包中有三个类用于同步一批线程的行为,分别是CountDownLatch、Semaphore和CyclicBarrier。

CountDownLatch

 
 

CountDownLatch是一个计数器闭锁,通过它可以完成类似于阻塞当前线程的功能,即:一个线程或多个线程一直等待,直到其他线程执行的操作完成。CountDownLatch用一个给定的计数器来初始化,该计数器的操作是原子操作,即同时只能有一个线程去操作该计数器。调用该类await方法的线程会一直处于阻塞状态,直到其他线程调用countDown方法使当前计数器的值变为零,每次调用countDown计数器的值减1。当计数器值减至零时,所有因调用await()方法而处于等待状态的线程就会继续往下执行。这种现象只会出现一次,因为计数器不能被重置,如果业务上需要一个可以重置计数次数的版本,可以考虑使用CycliBarrier。

在某些业务场景中,程序执行需要等待某个条件完成后才能继续执行后续的操作;典型的应用如并行计算,当某个处理的运算量很大时,可以将该运算任务拆分成多个子任务,等待所有的子任务都完成之后,父任务再拿到所有子任务的运算结果进行汇总。

 1 import lombok.extern.slf4j.Slf4j;
 2 
 3 import java.util.concurrent.CountDownLatch;
 4 import java.util.concurrent.ExecutorService;
 5 import java.util.concurrent.Executors;
 6 
 7 @Slf4j
 8 public class CountDownLatchExample1 {
 9 
10     private final static int threadCount = 200;
11 
12     public static void main(String[] args) throws Exception {
13 
14         ExecutorService exec = Executors.newCachedThreadPool();
15 
16         final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
17 
18         for (int i = 0; i < threadCount; i++) {
19             final int threadNum = i;
20             exec.execute(() -> {
21                 try {
22                     test(threadNum);
23                 } catch (Exception e) {
24                     log.error("exception", e);
25                 } finally {
26                     countDownLatch.countDown();
27                 }
28             });
29         }
30         countDownLatch.await();
31         log.info("finish");
32         exec.shutdown();
33     }
34 
35     private static void test(int threadNum) throws Exception {
36         Thread.sleep(100);
37         log.info("{}", threadNum);
38         Thread.sleep(100);
39     }
 }

 

结果:

 1 20:18:32.917 [pool-1-thread-7] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample1 - 6
 2 20:18:32.917 [pool-1-thread-6] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample1 - 5
 3 20:18:32.919 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample1 - 4
 4 20:18:32.918 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample1 - 0
 5 20:18:32.918 [pool-1-thread-3] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample1 - 2
 6 20:18:32.916 [pool-1-thread-9] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample1 - 8
 7 20:18:32.918 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample1 - 3
 8 20:18:32.916 [pool-1-thread-10] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample1 - 9
 9 20:18:32.916 [pool-1-thread-8] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample1 - 7
10 20:18:32.917 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample1 - 1
11 20:18:33.032 [main] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample1 - finish

 

 1 import java.util.concurrent.CountDownLatch;
 2 import java.util.concurrent.ExecutorService;
 3 import java.util.concurrent.Executors;
 4 import java.util.concurrent.TimeUnit;
 5 
 6 @Slf4j
 7 public class CountDownLatchExample2 {
 8 
 9     private final static int threadCount = 200;
10 
11     public static void main(String[] args) throws Exception {
12 
13         ExecutorService exec = Executors.newCachedThreadPool();
14 
15         final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
16 
17         for (int i = 0; i < threadCount; i++) {
18             final int threadNum = i;
19             exec.execute(() -> {
20                 try {
21                     test(threadNum);
22                 } catch (Exception e) {
23                     log.error("exception", e);
24                 } finally {
25                     countDownLatch.countDown();
26                 }
27             });
28         }
29         countDownLatch.await(10, TimeUnit.MILLISECONDS);
30         log.info("finish");
31         exec.shutdown();
32     }
33 
34     private static void test(int threadNum) throws Exception {
35         Thread.sleep(100);
36         log.info("{}", threadNum);
37     }
38 }

 

结果: 超过指定时间跳过等待

 1 20:19:34.878 [main] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample2 - finish
 2 20:19:34.964 [pool-1-thread-3] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample2 - 2
 3 20:19:34.965 [pool-1-thread-10] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample2 - 9
 4 20:19:34.964 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample2 - 0
 5 20:19:34.965 [pool-1-thread-8] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample2 - 7
 6 20:19:34.964 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample2 - 1
 7 20:19:34.965 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample2 - 4
 8 20:19:34.965 [pool-1-thread-7] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample2 - 6
 9 20:19:34.964 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample2 - 3
10 20:19:34.965 [pool-1-thread-9] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample2 - 8
11 20:19:34.965 [pool-1-thread-6] INFO com.mmall.concurrency.example.aqs.CountDownLatchExample2 - 5

 

Semaphore

 
 

Semaphore与CountDownLatch相似,不同的地方在于Semaphore的值被获取到后是可以释放的,并不像CountDownLatch那样一直减到底。它也被更多地用来限制流量,类似阀门的 功能。如果限定某些资源最多有N个线程可以访问,那么超过N个主不允许再有线程来访问,同时当现有线程结束后,就会释放,然后允许新的线程进来。有点类似于锁的lock与 unlock过程。相对来说他也有两个主要的方法:

用于获取权限的acquire(),其底层实现与CountDownLatch.countdown()类似;
用于释放权限的release(),其底层实现与acquire()是一个互逆的过程。

 1 import lombok.extern.slf4j.Slf4j;
 2 
 3 import java.util.concurrent.CountDownLatch;
 4 import java.util.concurrent.ExecutorService;
 5 import java.util.concurrent.Executors;
 6 import java.util.concurrent.Semaphore;
 7 
 8 @Slf4j
 9 public class SemaphoreExample1 {
10 
11     private final static int threadCount = 20;
12 
13     public static void main(String[] args) throws Exception {
14 
15         ExecutorService exec = Executors.newCachedThreadPool();
16        // 每次最多三个线程获取许可
17         final Semaphore semaphore = new Semaphore(3);
18 
19         for (int i = 0; i < threadCount; i++) {
20             final int threadNum = i;
21             exec.execute(() -> {
22                 try {
23                     semaphore.acquire(); // 获取一个许可
24                     test(threadNum);
25                     semaphore.release(); // 释放一个许可
26                 } catch (Exception e) {
27                     log.error("exception", e);
28                 }
29             });
30         }
31         exec.shutdown();
32     }
33 
34     private static void test(int threadNum) throws Exception {
35         log.info("{}", threadNum);
36         Thread.sleep(1000);
37     }
38 }

 

 1 import java.util.concurrent.ExecutorService;
 2 import java.util.concurrent.Executors;
 3 import java.util.concurrent.Semaphore;
 4 
 5 @Slf4j
 6 public class SemaphoreExample2 {
 7 
 8     private final static int threadCount = 20;
 9 
10     public static void main(String[] args) throws Exception {
11 
12         ExecutorService exec = Executors.newCachedThreadPool();
13 
14         final Semaphore semaphore = new Semaphore(3);
15 
16         for (int i = 0; i < threadCount; i++) {
17             final int threadNum = i;
18             exec.execute(() -> {
19                 try {
20                     semaphore.acquire(3); // 获取多个许可
21                     test(threadNum);
22                     semaphore.release(3); // 释放多个许可
23                 } catch (Exception e) {
24                     log.error("exception", e);
25                 }
26             });
27         }
28         exec.shutdown();
29     }
30 
31     private static void test(int threadNum) throws Exception {
32         log.info("{}", threadNum);
33         Thread.sleep(1000);
34     }
35 }

 

 1 import lombok.extern.slf4j.Slf4j;
 2 
 3 import java.util.concurrent.ExecutorService;
 4 import java.util.concurrent.Executors;
 5 import java.util.concurrent.Semaphore;
 6 import java.util.concurrent.TimeUnit;
 7 
 8 @Slf4j
 9 public class SemaphoreExample3 {
10 
11     private final static int threadCount = 20;
12 
13     public static void main(String[] args) throws Exception {
14 
15         ExecutorService exec = Executors.newCachedThreadPool();
16 
17         final Semaphore semaphore = new Semaphore(3);
18 
19         for (int i = 0; i < threadCount; i++) {
20             final int threadNum = i;
21             exec.execute(() -> {
22                 try {
23                     if (semaphore.tryAcquire()) { // 尝试获取一个许可
24                         test(threadNum);
25                         semaphore.release(); // 释放一个许可
26                     }
27                 } catch (Exception e) {
28                     log.error("exception", e);
29                 }
30             });
31         }
32         exec.shutdown();
33     }
34 
35     private static void test(int threadNum) throws Exception {
36         log.info("{}", threadNum);
37         Thread.sleep(1000);
38     }
39 }

 

 1 import lombok.extern.slf4j.Slf4j;
 2 
 3 import java.util.concurrent.ExecutorService;
 4 import java.util.concurrent.Executors;
 5 import java.util.concurrent.Semaphore;
 6 import java.util.concurrent.TimeUnit;
 7 
 8 @Slf4j
 9 public class SemaphoreExample4 {
10 
11     private final static int threadCount = 20;
12 
13     public static void main(String[] args) throws Exception {
14 
15         ExecutorService exec = Executors.newCachedThreadPool();
16 
17         final Semaphore semaphore = new Semaphore(3);
18 
19         for (int i = 0; i < threadCount; i++) {
20             final int threadNum = i;
21             exec.execute(() -> {
22                 try {
23                     if (semaphore.tryAcquire(5000, TimeUnit.MILLISECONDS)) { // 尝试获取一个许可
24                         test(threadNum);
25                         semaphore.release(); // 释放一个许可
26                     }
27                 } catch (Exception e) {
28                     log.error("exception", e);
29                 }
30             });
31         }
32         exec.shutdown();
33     }
34 
35     private static void test(int threadNum) throws Exception {
36         log.info("{}", threadNum);
37         Thread.sleep(1000);
38     }
39 }

 

CyclicBarrier

 
 

CyclicBarrier也是一个同步辅助类,它允许一组线程相互等待,直到到达某个公共屏障点(common barrier point)。通过它可以完成多个线程之间相互等待,只有当每个线程都准备就绪后,才能各自继续往下执行后面的操作。类似于CountDownLatch,它也是通过计数器来实现的。当某个线程调用await方法时,该线程进入等待状态,且计数器加1,当计数器的值达到设置的初始值时,所有因调用await进入等待状态的线程被唤醒,继续执行后续操作。因为CycliBarrier在释放等待线程后可以重用,所以称为循环barrier。CycliBarrier支持一个可选的Runnable,在计数器的值到达设定值后(但在释放所有线程之前),该Runnable运行一次,注,Runnable在每个屏障点只运行一个。

使用场景类似于CountDownLatch与CountDownLatch的区别

  • CountDownLatch主要是实现了1个或N个线程需要等待其他线程完成某项操作之后才能继续往下执行操作,描述的是1个线程或N个线程等待其他线程的关系。CyclicBarrier主要是实现了多个线程之间相互等待,直到所有的线程都满足了条件之后各自才能继续执行后续的操作,描述的多个线程内部相互等待的关系。
  • CountDownLatch是一次性的,而CyclicBarrier则可以被重置而重复使用。
 1 @Slf4j
 2 public class CyclicBarrierExample1 {
 3 
 4     private static CyclicBarrier barrier = new CyclicBarrier(5);
 5 
 6     public static void main(String[] args) throws Exception {
 7 
 8         ExecutorService executor = Executors.newCachedThreadPool();
 9 
10         for (int i = 0; i < 10; i++) {
11             final int threadNum = i;
12             Thread.sleep(1000);
13             executor.execute(() -> {
14                 try {
15                     race(threadNum);
16                 } catch (Exception e) {
17                     log.error("exception", e);
18                 }
19             });
20         }
21         executor.shutdown();
22     }
23 
24     private static void race(int threadNum) throws Exception {
25         Thread.sleep(1000);
26         log.info("{} is ready", threadNum);
27         barrier.await();
28         log.info("{} continue", threadNum);
29     }
30 }

 

结果: ready ready .. go

 1 20:24:34.616 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 0 is ready
 2 20:24:35.610 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 1 is ready
 3 20:24:36.610 [pool-1-thread-3] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 2 is ready
 4 20:24:37.611 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 3 is ready
 5 20:24:38.612 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 4 is ready
 6 20:24:38.612 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 0 continue
 7 20:24:38.612 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 1 continue
 8 20:24:38.612 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 4 continue
 9 20:24:38.612 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 3 continue
10 20:24:38.612 [pool-1-thread-3] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 2 continue
11 20:24:39.614 [pool-1-thread-6] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 5 is ready
12 20:24:40.613 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 6 is ready
13 20:24:41.614 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 7 is ready
14 20:24:42.615 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 8 is ready
15 20:24:43.615 [pool-1-thread-3] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 9 is ready
16 20:24:43.615 [pool-1-thread-3] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 9 continue
17 20:24:43.615 [pool-1-thread-6] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 5 continue
18 20:24:43.615 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 6 continue
19 20:24:43.615 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 7 continue
20 20:24:43.615 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 8 continue

 

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

@Slf4j
public class CyclicBarrierExample2 {

    private static CyclicBarrier barrier = new CyclicBarrier(5);

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

        ExecutorService executor = Executors.newCachedThreadPool();

        for (int i = 0; i < 10; i++) {
            final int threadNum = i;
            Thread.sleep(1000);
            executor.execute(() -> {
                try {
                    race(threadNum);
                } catch (Exception e) {
                    log.error("exception", e);
                }
            });
        }
        executor.shutdown();
    }

    private static void race(int threadNum) throws Exception {
        Thread.sleep(1000);
        log.info("{} is ready", threadNum);
        try {
            barrier.await(2000, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            log.warn("BarrierException", e);
        }
        log.info("{} continue", threadNum);
    }
}

 

import lombok.extern.slf4j.Slf4j;

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

@Slf4j
public class CyclicBarrierExample3 {

    private static CyclicBarrier barrier = new CyclicBarrier(5, () -> {
        log.info("callback is running");
    });

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

        ExecutorService executor = Executors.newCachedThreadPool();

        for (int i = 0; i < 10; i++) {
            final int threadNum = i;
            Thread.sleep(1000);
            executor.execute(() -> {
                try {
                    race(threadNum);
                } catch (Exception e) {
                    log.error("exception", e);
                }
            });
        }
        executor.shutdown();
    }

    private static void race(int threadNum) throws Exception {
        Thread.sleep(1000);
        log.info("{} is ready", threadNum);
        barrier.await();
        log.info("{} continue", threadNum);
    }
}

 

结果:

20:28:32.790 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 0 is ready
20:28:33.785 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 1 is ready
20:28:34.786 [pool-1-thread-3] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 2 is ready
20:28:35.787 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 3 is ready
20:28:36.787 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 4 is ready
20:28:36.787 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - callback is running
20:28:36.787 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 4 continue
20:28:36.788 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 0 continue
20:28:36.788 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 1 continue
20:28:36.788 [pool-1-thread-3] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 2 continue
20:28:36.788 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 3 continue
20:28:37.788 [pool-1-thread-6] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 5 is ready
20:28:38.789 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 6 is ready
20:28:39.789 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 7 is ready
20:28:40.790 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 8 is ready
20:28:41.791 [pool-1-thread-3] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 9 is ready
20:28:41.791 [pool-1-thread-3] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - callback is running
20:28:41.791 [pool-1-thread-3] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 9 continue
20:28:41.791 [pool-1-thread-6] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 5 continue
20:28:41.791 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 6 continue
20:28:41.818 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 8 continue
20:28:41.818 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 7 continue