文章目录
-
一、什么是闭锁
* 二、CountDownLatch类介绍 -
2.1、什么是CountDownLatch
* 2.2、构造方法
* 2.3、主要方法1
21 * 三、闭锁的使用
2 -
1.一个计数器使用的例子:主线程等待其他所有线程执行完毕在执行
* 2.一个并发测试的例子:将所有的子线程全部阻塞在一个入口,等待主线程统一放行
本文将介绍什么是闭锁,在java中的闭锁实现:CountDownLatch类及其常用方法等,最后给出了一个使用闭锁模拟线程并发的demo,用以简单地测试任务是否为线程安全。
一、什么是闭锁
闭锁(Latch)是在并发编程中常被提及的概念。闭锁是一种线程控制对象,它能让所有的线程在某个状态时终止工作并等待,直到闭锁“开门”时,所有的线程在这一刻会几乎同时执行工作,制造出一个并发的环境。
二、CountDownLatch类介绍
2.1、什么是CountDownLatch
CountDownLatch,顾名思义,可以理解为计数(count)、减少(down)、闭锁(Latch),即通过计数减少的方式来达到阻碍线程执行任务的一种闭锁,这个类位于java.util.concurent并发包下,是java中闭锁的最优实现。
2.2、构造方法
CountDownLatch(int count)
构造器中计数值(count)就是闭锁需要等待的线程数量,这个值只能被设置一次。
2.3、主要方法
- void await():
使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
- boolean await(long timeout, TimeUnit unit):
使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。
- void countDown():
递减锁存器的计数,如果计数到达零,则释放所有等待的线程。
- long getCount():
返回当前计数。
- String toString():
返回标识此锁存器及其状态的字符串。
三、闭锁的使用
1.一个计数器使用的例子:主线程等待其他所有线程执行完毕在执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 1public class CountDownLatchDemo {
2 private static int counter = 0;
3
4 public static void main(String[] args) throws InterruptedException {
5 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
6 0, // corePoolSize
7 Integer.MAX_VALUE, // maxPoolSize
8 60L,
9 TimeUnit.SECONDS,
10 new SynchronousQueue<Runnable>()
11 );
12 int count = 5;
13 CountDownLatch latch = new CountDownLatch(count);
14 for (int i = 0; i < count; i++) {
15 threadPoolExecutor.execute(() -> {
16 try {
17 run();
18 } catch (Exception e) {
19 }finally {
20 latch.countDown();
21 }
22 });
23 }
24 latch.await();
25 threadPoolExecutor.shutdown();
26 System.out.println("线程执行完毕");
27 }
28
29 private static void run() throws InterruptedException {
30 Thread.sleep(1000);
31 System.out.println(Thread.currentThread());
32
33 }
34}
35
36
2.一个并发测试的例子:将所有的子线程全部阻塞在一个入口,等待主线程统一放行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 1public static void main(String[] args) throws InterruptedException {
2 ExecutorService executorService = Executors.newCachedThreadPool();
3 CountDownLatch countDownLatch = new CountDownLatch(1);
4 int i = 0;
5 for (i = 0; i <= 50; i++) {
6 final int num = i;
7 executorService.submit(()->{
8 try {
9 countDownLatch.await();
10 System.out.println(String.format("线程%s打印%d", Thread.currentThread().getName(), num));
11 } catch (InterruptedException e) {
12 e.printStackTrace();
13 }
14 });
15 }
16
17 Thread.sleep(5000);
18
19 System.out.println("FINISHED");
20 countDownLatch.countDown();
21 executorService.shutdown();
22 }
23
24
可以看到执行结果中,counter的值出现了并发冲突:
这时可以使用原子类AtomicInteger,通过CAS去自增计数器的值(为什么不使用volatile呢?因为volatile虽然能保证多线程读写共享变量的可见性,但修改一个变量并写入毕竟是分为读-改-写这三个过程的,所以仍需要使用CAS来保障)。
1
2
3
4
5
6
7 1private static AtomicInteger counter = new AtomicInteger();
2 private static void run() throws InterruptedException {
3 // 使用原子类 CAS更新值
4 System.out.println(Thread.currentThread() + ":" + counter.incrementAndGet());
5 }
6
7