目录
一、引言
在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch,CyclicBarrier、Semaphore和exchanger。
Semaphore:一个计数信号量
CountDownLatch:一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
CyclicBarrier:一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点
Exchanger:方便了两个共同操作线程之间的双向交换
下面举例子分别介绍这些类的使用场景
二、Semaphore
Semaphore 信号量对象管理的信号就像令牌,构造时传入个数,总数就是控制并发的数量。我们需要控制并发的代码,执行前先获取信号(通过acquire获取信号许可),执行后归还信号(通过release归还信号许可)。每次acquire成功返回后,Semaphore可用的信号量就会减少一个,如果没有可用的信号,acquire调用就会阻塞,等待有release调用释放信号后,acquire才会得到信号并返回。
如果Semaphore管理的信号量为1个,那么就退化到互斥锁了;如果多于一个信号量,则主要用于控制并发数。与通过控制线程数来控制并发数的方式相比,通过Semaphore来控制并发数可以控制得更加细粒度,因为真正被控制最大并发的代码放到acquire和release之间就行了。
使用场景,食堂做饭只有四个切菜刀,有十个人要切十个菜,限制菜刀数量抢到的先切菜,切完再给别人。
public class Testsemophore {
public static void main(String[]args){
final Semaphore semaphore = new Semaphore(3);
for (int i=0;i<7;i++)
{
new Thread(new Runnable() {
public void run() {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"获取到许可");
Thread.sleep(1000);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
输出结果:
Thread-0获取到许可
Thread-2获取到许可
Thread-1获取到许可
Thread-3获取到许可
Thread-4获取到许可
Thread-5获取到许可
Thread-6获取到许可
三、countdownlatch
一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
其机制是:当多个(具体数量等于初始化CountDownLatch时的count参数的值)线程都达到了预期状态或完成预期工作时触发事件,其他线程可以等待这个事件来触发自己的后续工作。这里需要注意的是,等待的线程可以是多个,即CountDownLatch是可以唤醒多个等待的线程的。达到自己预期状态的线程会调用CountDownLatch的countDown方法,而等待的线程会调用CountDownLatch的await方法。
CountDownLatch 很适合用来将一个任务分为n个独立的部分,等这些部分都完成后继续接下来的任务,CountDownLatch 只能出发一次,计数值不能被重置。
public class Testcutdownlatch {
public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(3);
new Thread(new Runnable() {
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Waiter Released");
}
}).start();
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(1000);
latch.countDown();
Thread.sleep(1000);
latch.countDown();
Thread.sleep(1000);
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
输出结果:
Waiter Released
四、cyclicbarrier
可以协同多个线程,让多个线程在这个屏障前等待,直到所有线程都达到了这个屏障时,再一起继续执行后面的动作。
CyclicBarrier适用于多个线程有固定的多步需要执行,线程间互相等待,当都执行完了,再一起执行下一步。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier
public class Testcyclicbarrier {
public static void main(String[] args) {
final CyclicBarrier barrier1 = new CyclicBarrier(2, new Runnable() {
public void run() {
System.out.println("BarrierAction 1 executed ");
}
});
final CyclicBarrier barrier2 = new CyclicBarrier(2, new Runnable() {
public void run() {
System.out.println("BarrierAction 2 executed ");
}
});
for (int i=0;i<3;i++)
{
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"waited barrier1");
barrier1.await();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"waited barrier2");
barrier2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
输出结果:
Thread-0waited barrier1
Thread-2waited barrier1
Thread-1waited barrier1
BarrierAction 1 executed
Thread-0waited barrier2
Thread-1waited barrier2
BarrierAction 2 executed
注意比较CountDownLatch和CyclicBarrier:
(01) CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待。
(02) CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。
CountDownLatch 适用于一组线程和另一个主线程之间的工作协作。一个主线程等待一组工作线程的任务完毕才继续它的执行是使用 CountDownLatch 的主要场景;CyclicBarrier 用于一组或几组线程,比如一组线程需要在一个时间点上达成一致,例如同时开始一个工作。
五、exchanger
Exchanger 类方便了两个共同操作线程之间的双向交换;这样,就像具有计数为 2 的 CyclicBarrier,并且两个线程在都到达屏障时可以“交换”一些状态。(Exchanger 模式有时也称为聚集。)
Exchanger 通常用于一个线程填充缓冲(通过读取 socket),而另一个线程清空缓冲(通过处理从 socket 收到的命令)的情况。当两个线程在屏障处集合时,它们交换缓冲。
public class Testexchanger {
public static void main(String[]args){
final Exchanger exchanger = new Exchanger();
//Action action=new Action();
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(1000);
exchanger.exchange(Thread.currentThread().getName()+"data");
System.out.println(Thread.currentThread().getName()+"我交给你了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
try {
exchanger.exchange(Thread.currentThread().getName()+"data");
System.out.println(Thread.currentThread().getName()+"我交给你了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
输出结果:
Thread-0我交给你了
Thread-1我交给你了
六、总结
- semaphore
- countdownlatch
- cyclicbarrier
- exchanger