Java并发编程(9)-CountDownLatch

释放双眼,带上耳机,听听看~!

文章目录

  • 一、什么是闭锁
    * 二、CountDownLatch类介绍

  • 2.1、什么是CountDownLatch
    * 2.2、构造方法
    * 2.3、主要方法

    
    
    1
    2
    1  * 三、闭锁的使用
    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

Java并发编程(9)-CountDownLatch

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的值出现了并发冲突:
Java并发编程(9)-CountDownLatch
这时可以使用原子类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

给TA打赏
共{{data.count}}人
人已打赏
安全技术

用node.js从零开始去写一个简单的爬虫

2021-12-21 16:36:11

安全技术

从零搭建自己的SpringBoot后台框架(二十三)

2022-1-12 12:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索