CountDownLatch
CountDownLatch是java.util.concurrent(jdk1.5加入并发包)包下的类。CountDownLatch主要提供的机制是当多个(具体的数量等于初始化CountDownLatch时的count参数的值)线程都达到了预期状态或者完成预期工作时触发事件,其他线程可以等待这个事件来触发自己后续的工作。这里需要注意的是,等待的线程可以使多个,即CountDownLatch是可以唤醒多个等待的线程的。到达自己预期状态的线程会调用CountDownLatch的countDown方法,而等待的线程会调用CountDownLatch的await方法
CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1,当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。
CountDownLatch中主要方法如下:
public CountDownLatch(int count),构造函数中的count(计数器)实际上就是闭锁需要等待的线程数量,这个值只能被设置一次,而且CountDownLatch没有提供任何机制去重新设置这个计数值。
public void countDown(),每调用一次这个方法,在构造函数中初始化的count值就减1,通知机制是此方法来完成的。
public void await() throws InterruptedException,调用此方法的当前线程会一直阻塞,直到计时器的值为0。
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws Exception {
CountDownLatch latch = new CountDownLatch(3);
Worker worker1 = new Worker("Jack 程序员1", latch);
Worker worker2 = new Worker("Rose 程序员2", latch);
Worker worker3 = new Worker("Json 程序员3", latch);
worker1.start();
worker2.start();
worker3.start();
latch.await();
System.out.println("Main thread end!");
}
static class Worker extends Thread {
private String workerName;
private CountDownLatch latch;
public Worker(String workerName, CountDownLatch latch) {
this.workerName = workerName;
this.latch = latch;
}
@Override
public void run() {
try {
System.out.println("Worker:" + workerName + " is begin.");
Thread.sleep(1000L);
System.out.println("Worker:" + workerName + " is end.");
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
}
}
}
结果
Worker:Json 程序员3 is begin.
Worker:Jack 程序员1 is begin.
Worker:Rose 程序员2 is begin.
Worker:Json 程序员3 is end.
Worker:Jack 程序员1 is end.
Worker:Rose 程序员2 is end.
Main thread end!
CyclicBarrier
CyclicBarrier :循环屏障,CyclicBarrier 可以协同多个线程,让多个线程在这个屏障前等待,直到所有线程都达到了这个屏障时,再一起继续执行后面的动作
从上图看出,三个线程中各有一个barrier.await(),那么任何一个线程在执行到barrier.await()时就会进入阻塞等待状态,直到三个线程都到了barrier.await时才会同时从await返回,继续后续的工作。此外,如果在构造CyclicBarrier 时设置了一个Runnable实现,那么最后一个到barrier.await的线程会执行这个Runnable的run方法,以完成一些预设的工作
public CyclicBarrier(int parties)
public CyclicBarrier(int parties, Runnable barrierAction)
实例:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static void main(String args[]) throws Exception {
CyclicBarrier barrier = new CyclicBarrier(3, new TotalTask());
BillTask worker1 = new BillTask("111", barrier);
BillTask worker2 = new BillTask("222", barrier);
BillTask worker3 = new BillTask("333", barrier);
worker1.start();
worker2.start();
worker3.start();
System.out.println("Main thread end!");
}
static class TotalTask extends Thread {
public void run() {
System.out.println("所有子任务都执行完了,就开始执行主任务了。");
}
}
static class BillTask extends Thread {
private String billName;
private CyclicBarrier barrier;
public BillTask(String workerName, CyclicBarrier barrier) {
this.billName = workerName;
this.barrier = barrier;
}
@Override
public void run() {
try {
System.out.println("市区:" + billName + "运算开始:");
Thread.sleep(1000L);// 模仿第一次运算;
System.out.println("市区:" + billName + "运算完成,等待中...");
barrier.await();// 假设一次运算不完,第二次要依赖第一次的运算结果。都到达这个节点之后后面才会继续执行;
System.out.println("全部都结束,市区" + billName + "才开始后面的工作。");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
结果:
市区:111运算开始:
Main thread end!
市区:222运算开始:
市区:333运算开始:
市区:111运算完成,等待中...
市区:222运算完成,等待中...
市区:333运算完成,等待中...
所有子任务都执行完了,就开始执行主任务了。//这句话是最后到达wait()方法的那个线程执行的
全部都结束,市区111才开始后面的工作。
全部都结束,市区333才开始后面的工作。
全部都结束,市区222才开始后面的工作。
在这个示例中,构造CyclicBarrier时,传入了内部类TotalTask(TotalTask继承了Thread,是Runnable的实现)的实例对象,其意义在于:当所有的线程都执行到await()方法时,它们会一起返回继续自己的工作,但是最后一个到达await()方法的线程会执行TotalTask的run()方法;如果在构造构造CyclicBarrier时没有传入Runnable的实现对象作为构造参数,则当所有的线程都执行到await()方法时会直接一起返回继续自己的工作。
(3)CyclicBarrier与CountDownLatch的区别
A、CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待;
B、CountDownLatch的计数器无法被重置;而CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。
C、参数parties指定线程数量,当指定的线程值都到达栅栏点时,栅栏打开,线程恢复。需要注意的是,当指定的线程数量大于启动的线程数量,比如修改上例中的代码,只启动9个线程,那么所有的线程将一直处于等待状态。第二种情况是指定的线程数量小于启动的线程,上例代码,启动11个线程,那么当第十个线程到达栅栏点时,那么这十个线程就会恢复继续执行,而第十一个线程将一直处于阻塞状态
Semaphore(信号量)
Java中的Semaphore用于在线程间传递信号,从概念上讲,信号量维护了一个许可集合,Semaphore只对可用的许可进行计数,并采取相应的行动。信号量常常用于多线程的代码中,比如数据库连接池。
semaphore 是用于管理信号量的,构造的时候传入可供管理的信号量数值,简单来说,信号量对象管理的信号就像令牌,构造时传入个数,总数是控制并发的数量。我们需要控制并发的代码,执行前先获取信号(通过acquire获取信号许可),执行后归还信号(通过release归还信号许可)。每次acquire成功返回后,semaphore 可用的信号量就会减少一个,如果没有可用的信号,acquire调用就会阻塞,等待有release 调用释放信号后,acquire 才会得到信号并返回
如果Semaphore 管理的信号量只有一个,那么就退化到互斥锁了;如果对于 1个信号量,则主要用于控制并发数,与通过控制线程数来控制数来控制并发数的方式比,通过Semaphore 来控制并发数可以控制得更加细粒度,因为真正被控制最大并发的代码放到acquire 和release 之间就行了
实例:
假设一个服务器资源有限,任意某一时刻只允许3个人同时进行访问,这时一共来了10个人
import java.util.concurrent.Semaphore;
public class SemaphoreDemo {
public static void main(String args[]) throws Exception {
final Semaphore semaphore = new Semaphore(3);// 一次只运行3个人进行访问
for (int i = 0; i < 10; i++) {
final int no = i;
Runnable thread = new Runnable() {
public void run() {
try {
System.out.println("用户" + no + "连接上了:");
Thread.sleep(300L);
semaphore.acquire();// 获取接下去执行的许可
System.out.println("用户" + no + "开始访问后台程序...");
Thread.sleep(1000L);// 模仿用户访问服务过程
semaphore.release();// 释放允许下一个线程访问进入后台
System.out.println("用户" + no + "访问结束。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
new Thread(thread).start();
}
System.out.println("Main thread end!");
}
}
结果
用户0连接上了:
用户2连接上了:
用户4连接上了:
用户1连接上了:
Main thread end!
用户6连接上了:
用户5连接上了:
用户7连接上了:
用户9连接上了:
用户8连接上了:
用户3连接上了:
用户0开始访问后台程序...
用户2开始访问后台程序...
用户4开始访问后台程序...
用户0访问结束。
用户1开始访问后台程序...
用户6开始访问后台程序...
用户2访问结束。
用户4访问结束。
用户5开始访问后台程序...
用户1访问结束。
用户7开始访问后台程序...
用户6访问结束。
用户9开始访问后台程序...
用户5访问结束。
用户8开始访问后台程序...
用户7访问结束。
用户3开始访问后台程序...
用户9访问结束。
用户8访问结束。
用户3访问结束。
从结果上可以看出来,10个人同时进来,但是只能同时3个人访问资源,释放一个允许进来一个
Exchanger
Exchanger类源于java.util.concurrent包,它可以在两个线程之间传输数据,Exchanger中的public V exchange(V x)
方法被调用后等待另一个线程到达交换点(如果当前线程没有被中断),然后将已知的对象传给它,返回接收的对象。如果另外一个线程已经在交换点等待,那么恢复线程计划并接收通过当前线程传给的对象:
实例:
import java.util.concurrent.Exchanger;
public class Car extends Thread {
private Exchanger<String> exchanger;
public Car(Exchanger<String> exchanger) {
super();
this.exchanger = exchanger;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + ": "
+ exchanger.exchange("Car"));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Exchanger<String> exchanger = new Exchanger<>();
Car car = new Car(exchanger);
Bike bike = new Bike(exchanger);
car.start();
bike.start();
System.out.println("Main end!");
}
}
class Bike extends Thread {
private Exchanger<String> exchanger;
public Bike(Exchanger<String> exchanger) {
super();
this.exchanger = exchanger;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + ": "
+ exchanger.exchange("Bike"));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果
Main end!
Thread-0: Bike
Thread-1: Car
参考资料:
http://ifeve.com/java-memory-model-1/