并发工具类:CountDownLatch、CyclicBarrier、Semaphore

在多线程的场景下,有些并发流程需要人为来控制,在JDK的并发包里提供了几个并发工具类:CountDownLatch、CyclicBarrier、Semaphore。

一、CountDownLatch

 1 import java.util.concurrent.CountDownLatch;
 2
 3
 4 public class CountDownLatchTest
 5 {    //设置N为2
 6     static CountDownLatch c = new CountDownLatch(2);
 7     public static void main(String[] args) throws Exception
 8     {
 9         Thread t1 = new Thread(new Runnable()
10         {
11
12             @Override
13             public void run()
14             {
15                 System.out.println("1");           //将N减1
16                 c.countDown();
17             }
18         });
19
20         Thread t2 = new Thread(new Runnable()
21         {
22
23             @Override
24             public void run()
25             {
26                 System.out.println("2");
27                 c.countDown();
28             }
29         });
30         t1.start();
31         t2.start();      //让当前线程等待,直到计数N为0
32         c.await();
33         System.out.println("3");
34     }
35 }

执行结果:

1
2
3

这里会存在两种结果:123或者213,但是绝对不会出现3打印在1、2前面的。

new CountDownLatch(2);

这个CountDownLatch的构造函数接收一个int类型的参数作为计数器,N表示阻塞的线程必须等待N次countDown才能执行。

每次调用CountDownLatch的countDown方法时,N就会减1,而这个方法可以使用在任何地方,这里的N点可以是N个线程,也可以是一个线程中N个步骤。

而CountDownLatch的await方法则会阻塞当前线程,直到N为0的时候才能执行。

我们将上面的程序改造下,让线程中有两个打印动作,并且第二个动作前线程休眠一段时间:

 1 import java.util.concurrent.CountDownLatch;
 2
 3
 4 public class CountDownLatchTest
 5 {
 6     static CountDownLatch c = new CountDownLatch(2);
 7     public static void main(String[] args) throws Exception
 8     {
 9         Thread t1 = new Thread(new Runnable()
10         {
11
12             @Override
13             public void run()
14             {
15                 System.out.println("1");
16                 c.countDown();
17                 try
18                 {
19                     Thread.sleep(500);
20                     System.out.println("2");
21                 }
22                 catch (InterruptedException e)
23                 {
24                     e.printStackTrace();
25                 }
26             }
27         });
28
29         Thread t2 = new Thread(new Runnable()
30         {
31
32             @Override
33             public void run()
34             {
35                 System.out.println("3");
36                 c.countDown();
37             }
38         });
39         t1.start();
40         t2.start();
41         c.await();
42         System.out.println("4");
43     }
44 }

执行结果:

1
3
4
2

这个结果是由于在打印完1、3之后,N已经变化为0,主线程执行打印4,由于线程1休眠,所以2最后才打印。

在上面的程序中,如果将N设置为3,则主线程中的打印4永远不会执行,因为没有N永远只会到1而不会减少到0.

在这里我们想起了线程的join方法,这个方法也是可以阻塞当前线程,等待某线程执行完成。通过对比,我们可以发现使用CountDownLatch这个工具类更灵活,因为countDown可以用在任何线程的任何地方。

CountDownLatch适合一个大任务拆分成多个小任务,然后在所有子任务完成后,通知其他的后续操作开始执行。

二、同步屏障CyclicBarrier

CyclicBarrier默认的构造方法CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达屏障,然后当前线程被阻塞,直到被拦截的线程全部都到达了屏障,然后前面被阻塞的线程才能开始执行,否则会被一直阻塞。

 1 public class CyclicBarrierTest
 2 {
 3     static CyclicBarrier c = new CyclicBarrier(3);
 4
 5     public static void main(String[] args)
 6         throws Throwable, BrokenBarrierException
 7     {
 8         Thread t1 = new Thread(new Runnable()
 9         {
10             @Override
11             public void run()
12             {
13                 try
14                 {
15                     c.await();
16                 }
17                 catch (InterruptedException e)
18                 {
19                     e.printStackTrace();
20                 }
21                 catch (BrokenBarrierException e)
22                 {
23                     e.printStackTrace();
24                 }
25                 System.out.println("1");
26
27             }
28         });
29         Thread t2 = new Thread(new Runnable()
30         {
31             @Override
32             public void run()
33             {
34                 try
35                 {
36                     c.await();
37                 }
38                 catch (InterruptedException e)
39                 {
40                     e.printStackTrace();
41                 }
42                 catch (BrokenBarrierException e)
43                 {
44                     e.printStackTrace();
45                 }
46                 System.out.println("2");
47             }
48         });
49
50         t1.start();
51         t2.start();
52         c.await();
53         System.out.println("3");
54     }
55 }

执行结果:

3
1
2

上述中被屏障拦截的线程有3个,其中线程1和线程2执行的时候先到达屏障,然后被阻塞,主线程执行第52行到达屏障,至此阻塞的三个线程全部到达屏障,然后阻塞的线程可以去竞争CPU开始执行。

如果将拦截的线程数修改为4:

static CyclicBarrier c = new CyclicBarrier(4);

这样的话被拦截的线程数有4个,但是只有三个线程调用await方法告诉CyclicBarrier,我到达了屏障。所以这三个线程都会被阻塞。

另外还有一点就是CyclicBarrier的计数器可以重置,例如设置的是拦截线程数量为2,但是有3个线程调用了await()方法表示到达了屏障,这个时候会出现最先达到屏障的两个线程顺利执行完毕,而最后到达的第三个线程则一直被阻塞,因为它等不到另外一个线程到达屏障了。

而如果拦截线程的数量依旧为2,但是有4个线程调用了await()方法,那么这4个线程是分两批执行的,前两个线程满足拦截的线程数,到达屏障后放行;然后CyclicBarrier的计数器重置,后面两个线程到达屏障后放行。

 1 import java.util.concurrent.BrokenBarrierException;
 2 import java.util.concurrent.CyclicBarrier;
 3
 4 import sun.java2d.SunGraphicsEnvironment.T1Filter;
 5
 6
 7 public class CyclicBarrierTest
 8 {
 9     static CyclicBarrier c = new CyclicBarrier(2);
10
11     public static void main(String[] args) throws Throwable, BrokenBarrierException
12     {
13         Thread t1 = new Thread(new Runnable()
14         {
15             @Override
16             public void run()
17             {
18                 try
19                 {
20                     c.await();
21                 }
22                 catch (InterruptedException e)
23                 {
24                     e.printStackTrace();
25                 }
26                 catch (BrokenBarrierException e)
27                 {
28                     e.printStackTrace();
29                 }
30                 System.out.println("1");
31
32             }
33         });
34         Thread t2 = new Thread(new Runnable()
35         {
36             @Override
37             public void run()
38             {
39                 try
40                 {
41                     c.await();
42                 }
43                 catch (InterruptedException e)
44                 {
45                     e.printStackTrace();
46                 }
47                 catch (BrokenBarrierException e)
48                 {
49                     e.printStackTrace();
50                 }
51                 System.out.println("2");
52             }
53         });
54
55         Thread t3 = new Thread(new Runnable()
56         {
57             @Override
58             public void run()
59             {
60                 try
61                 {
62                     c.await();
63                 }
64                 catch (InterruptedException e)
65                 {
66                     e.printStackTrace();
67                 }
68                 catch (BrokenBarrierException e)
69                 {
70                     e.printStackTrace();
71                 }
72                 System.out.println("3");
73
74             }
75         });
76         t1.start();
77         t2.start();
78         t3.start();
79         c.await();
80         System.out.println("4");
81     }
82 }

CyclicBarrier可以用于多线程计算数据,最后合并结果的场景;由于CyclicBarrier的计数器可以重置,所以可以使用它处理更为复杂的业务场景,而CountDownLatch计数器只能使用一次。

三、信号量Semaphore

无论是内部锁synchronized还是重入锁ReentrantLock,一次都只允许一个线程访问某一个资源,而信号量却可以指定多个线程同时访问某一资源;主要用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。

 1 public class SemaphoreTest implements Runnable
 2 {
 3     final Semaphore s = new Semaphore(5);
 4     @Override
 5     public void run()
 6     {
 7         try
 8         {
 9             s.acquire();
10             Thread.sleep(1000);
11             System.out.println(Thread.currentThread().getName() + " is done");
12             s.release();
13         }
14         catch (InterruptedException e)
15         {
16             e.printStackTrace();
17         }
18     }
19 }
public class MainTest
{
    public static void main(String[] args)
    {
        SemaphoreTest s = new SemaphoreTest();
        //创建一个可重用固定线程数的线程池,线程数量为20
        ExecutorService threadPool= Executors.newFixedThreadPool(20);
        for(int i=0;i<20;i++)
        {
            threadPool.submit(s);
        }
        threadPool.shutdown();
    }
}

执行的结果是每五个线程为一组打印消息。

线程池里面有20个可重复使用的线程数量,但是信号量只有5个,也就是每次只能并发5个线程执行,其他线程阻塞。

信号量为5,可以认为线程池里有5把锁,每个线程调用acquire和release分别表示获取锁和释放锁,这样,通过信号量就可以调度多个线程的执行。

时间: 2024-10-10 03:04:14

并发工具类:CountDownLatch、CyclicBarrier、Semaphore的相关文章

Java并发工具类 - CountDownLatch

Java并发工具类 - CountDownLatch 1.简介 CountDownLatch是Java1.5之后引入的Java并发工具类,放在java.util.concurrent包下面 http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html 官方API. CountDownLatch能够使一个或多个线程等待其他线程完成各自的工作后再执行:CountDownLatch是JDK 5+里面

25.大白话说java并发工具类-CountDownLatch,CyclicBarrier,Semaphore,Exchanger

1. 倒计时器CountDownLatch 在多线程协作完成业务功能时,有时候需要等待其他多个线程完成任务之后,主线程才能继续往下执行业务功能,在这种的业务场景下,通常可以使用Thread类的join方法,让主线程等待被join的线程执行完之后,主线程才能继续往下执行.当然,使用线程间消息通信机制也可以完成.其实,java并发工具类中为我们提供了类似"倒计时"这样的工具类,可以十分方便的完成所说的这种业务场景. 为了能够理解CountDownLatch,举一个很通俗的例子,运动员进行跑

java同步并发工具类CountDownLatch、CyclicBarrier和Semaphore

闭锁CountDownLatch 闭锁是一种同步工具类,可以延迟线程的进度直到其到达终止状态.闭锁的作用相当于一扇门:在闭锁到达结束状态之前,这扇门一直是关闭的,并且没有任何线程能通过,当到达结束状态时,这扇门会打开并允许所有的线程通过.当闭锁到达结束状态后,将不会再改变状态,因此这扇门将永远保持打开状态.闭锁可以用来确保某些活动直到其他活动都完成后才继续执行,例如: 确保某个计算在其需要的所有资源都被初始化之后才继续执行.二元闭锁(包括两个状态)可以用来表示"资源R已经被初始化",而

1.3.4 并发工具类CountDownLatch/Semaphore/CyclicBarrier/FutureTask

CountDownLatch的2个用途: 1. 所有线程都到达相同的起跑线后,再一起开始跑(并非同时开始,而是队列中一个唤醒另一个)[此情况需到达起跑线后再调用await()等待其他线程] 2. 所有线程都到达终点(执行完)后,再一起庆祝 (并非同时开始,而是队列中一个唤醒另一个)[此情况需到达起终点后再调用await()等待其他线程] package com.study.concurrent_utils; import java.util.concurrent.CountDownLatch;

j.u.c系列(09)---之并发工具类:CyclicBarrier

写在前面 CyclicBarrier是一个同步辅助类,允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point).因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier. 注意比较CountDownLatch和CyclicBarrier: (01) CountDownLatch的作用是允许1或N个线程等待其他线程完成执行:而CyclicBarrier则是允许N个线程相互等待. (02) CountDownLatch的计数器无法被重置:

【死磕Java并发】-----J.U.C之并发工具类:CyclicBarrier

此篇博客所有源码均来自JDK 1.8 CyclicBarrier,一个同步辅助类,在API中是这么介绍的: 它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point).在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用.因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier. 通俗点讲就是:让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线

同步工具类 CountDownLatch 和 CyclicBarrier

在开发中,一些异步操作会明显加快执行速度带来更好的体验,但同时也增加了开发的复杂度,想了用好多线程,就必须从这些方面去了解 线程的 wait() notify() notifyall() 方法 线程异步返回 Future ThreadLocal 类 线程池 ThreadPoolExecutor 同步工具类 CountDownLatch,CyclicBarrier,Semaphore,Phaser,Exchanger 估计上面每一个对于 2~3 年的 java 同学来说都是恶梦,比较难以理解,本文

Java并发编程-线程的并发工具类

Fork-Join 什么是分而治之?规模为N的问题,N<阈值,直接解决,N>阈值,将N分解为K个小规模子问题,子问题互相对立,与原问题形式相同,将子问题的解合并得到原问题的解动态规范工作密取workStealing Fork/Join使用的标准范式 常用的并发工具类CountDownLatch作用:是一组线程等待其他的线程完成工作以后在执行,加强版joinawait用来等待,countDown负责计数器的减一CyclicBarrier让一组线程达到某个屏障,被阻塞,一直到组内最后一个线程达到屏

并发编程(2)--线程的并发工具类

1.线程的并发工具类 Fork-Join 什么是分而治之? 规模为N的问题,N<阈值,直接解决,N>阈值,将N分解为K个小规模子问题,子问题互相对立,与原问题形式相同,将子问题的解合并得到原问题的解 动态规范 工作密取 workStealing Fork/Join使用的标准范式 下面演示第一种用法:由于上下文切换的原因,所以性能上有可能不如单线程效果好. package com.xiangxue.ch2.forkjoin.sum; import java.util.Random; /** *