同步工具类:通过自身的状态,方法协调线程的控制流。阻塞队列就属于同步工具类,还包括信号量,栅栏以及闭锁。
闭锁式一种同步工具类。可以延迟线程的进度知道其达到终点状态(《java 并发编程实战》中的定义)。闭锁形象的可以理解为一扇门,当满足某个条件的时候门打开。常用于确保某些活动在其他活动都完成之后执行。
CountDownLatch 是一种灵活的闭锁的实现,内部有一个计数器。常用的方法:在构造CountDownLatch 对象的时候要传递一个计数器初始化值,表示要等待的事件数量,countDown()
每执行一次计数器就减一,执行await
方法时,会检查计数器是否为零,如果不为零,则await 会阻塞,如果为零那么表示所有需要等待的方法都已经执行,那么之前阻塞的方法就会执行。
案例分析:
比如:老板进入会议室等待 5 个人全部到达会议室才会开会。所以这里有两种线程:老板等待开会线程、员工到达会议室线程:
public class CountDownLatchTest {
private static CountDownLatch countDownLatch = new CountDownLatch(5);
/**
* Boss线程,等待员工到达开会
*/
static class BossThread extends Thread{
@Override
public void run() {
System.out.println("Boss在会议室等待,总共有" + countDownLatch.getCount() + "个人开会...");
try {
//Boss等待
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("所有人都已经到齐了,开会吧...");
}
}
// 员工到达会议室线程
static class EmpleoyeeThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ",到达会议室....");
//员工到达会议室 count - 1
countDownLatch.countDown();
}
}
public static void main(String[] args){
//Boss线程启动
new BossThread().start();
for(int i = 0 ; i < countDownLatch.getCount() ; i++){
new EmpleoyeeThread().start();
}
}
}
案例二:所有线程都准备好之后才能执行,并且统计所有线程执行的时间
public class TestHarness {
public long taskReady_Time(int nThread,final Runnable task) throws InterruptedException {
//起始门:保证主线程可以同时释放所有等待线程
final CountDownLatch startGate = new CountDownLatch(1);
//结束门:保证主线程可以等到所有线程执行完
final CountDownLatch endGate = new CountDownLatch(nThread);
for(int i = 0;i<nThread;i++){
Thread t = new Thread(){
public void run(){
try {
startGate.await();
try{
task.run();
}finally {
endGate.countDown();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t.start();
System.out.println(i);
}
System.out.println("*");
long start = System.nanoTime(); //单位纳秒
//释放所有等待线程
startGate.countDown();
//有效保证所有线程执行完
endGate.await();
long end = System.nanoTime();
System.out.println("*");
return end - start;
}
}
//测试
public class Test {
public static void main(String[] args) throws InterruptedException {
final Thread tt = new Thread(){
@Override
public void run() {
System.out.println("Thread Id: " + this.getId());
}
};
TestHarness bisuo = new TestHarness();
long threadtime = bisuo.taskReady_Time(100,tt);
double ans = (double)threadtime / 1000000000;
System.out.println("Thread time:" +ans + "s");
}
}
运行结果(结果太长,稍作裁剪:数字和* 号的输出是为了明白这段程序)
栅栏:类似闭锁,能阻塞一组线程知道某个时间发生。
栅栏和闭锁的区别:闭锁允许一个或者多个线程等待其他线程执行完成,但是栅栏只允许n 个线程相互等待
栅栏的常用情景,将一个复杂的问题拆分为几个简单的问题最后汇总得出结果。例如:计算文件中所有数据相加的和可以每个线程完成每一行数据相加的和最后汇总。这个例子就可以使用栅栏完成
实现类:CyclicBarreir
构造方法:CyclicBarrier(int parties, Runnable barrierAction)
:创建一个新的 CyclicBarrier,当给定数量的参与者都到达给定屏障时启动,执行barrierAcion(处理相关的业务逻辑)
await 方法和CountDownLatch 中的await 方法不同,线程调用await 方法时,告诉CyclicBarreir 我已经到达屏障,然后await阻塞等待其他的参与者到达屏障,结束阻塞,然后所有线程
代码实例:开会的时候要人员到齐了才能开始开会
public class CyclicBarrierTest {
private static CyclicBarrier cyclicBarrier ;
static class Empleey extends Thread{
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+"...到达会议室");
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args){
cyclicBarrier = new CyclicBarrier(5, new Runnable() {
@Override
public void run() {
System.out.println("人员到齐开始会议");
}
});
for (int i=0;i<5;i++){
new Empleey().start();
}
}
}
用来控制访问某个特定资源的操作数量,还可以用来实现某种资源池,或者对容器添加边界
常用实现类:Semaphore
Semaphore 管理着一组虚拟的许可,许可的初始量可以通过构造函数确定,方法acquire
获取许可,release
归还许可,当初始化构造为1的时候就是一个互斥信号量
Semaphore 可以用于实现池资源,构造一个固定长度的资源池,当请求资源失败我们希望的是阻塞而不是失败。首先请求一个资源acquire ,使用之后在释放资源release。(blocking可以更简单的实现池资源)
构造池对象:
public class BoundHashSet<T> {
private final Set<T> set;
private final Semaphore semaphore;
public BoundHashSet(){
set = new HashSet<T>();
semaphore = new Semaphore(10);//规定池对象边界
}
public boolean add(T o) throws InterruptedException {
semaphore.acquire();
boolean wasAdd = false;
try{
wasAdd = set.add(o);
return wasAdd;
}finally {
if(!wasAdd){
//在添加元素出现异常的情况下也要释放许可
semaphore.release();
}
}
}
public boolean remove(Object o){
boolean wasRemove = set.remove(0);
if(wasRemove){
//移除数据成功释放许可
semaphore.release();
}
return wasRemove;
}
}