并发工具类
CountDownLatch
简介
CountDownLatch 允许一个或多个线程等待其他线程完成操作。
使用场景
例如:班长命令士兵去修筑防御工事(士兵可以是一个也可以是多个),每个士兵负责自己的任务,等他们都执行完成后向班长汇报,班长向上级汇报防御工事修筑完毕。这个例子中班长等待士兵完成任务后,就是一个形象的CountDownLatch例子。
常用方法
-
await():调用这个方法线程等待。
-
countDown():调用这个方法等待完成的任务完成后减1,(内部计数器的值减1)。
与join()方法比较
CountDownLatch可以实现join()方法的功能,可以替换join()方法。
CyclicBarrier
简介
CyclicBarrier 让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。
使用场景
例如:班长准备修筑防御工事,这个时候士兵A报到,士兵B报到知道所有的士兵都到了,大家一起去修筑,不能A士兵到了就去修筑防御工事。等大家都到了班长还得进行讲话动员,这个就是CyclicBarrier(int parties, Runnable barrierAction)中barrierAction,动员后大家都打了鸡血似的去干活。
常用方法
-
int await() 在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。
-
int await(long timeout, TimeUnit unit) 在所有参与者都已经在此屏障上调用 await 方法之前,将一直等待。
-
int getNumberWaiting() 返回当前在屏障处等待的参与者数目。
-
int getParties() 返回要求启动此 barrier 的参与者数目。
-
boolean isBroken() 查询此屏障是否处于损坏状态。
-
void reset() 将屏障重置为其初始状态。
备注:int await(long timeout, TimeUnit unit),设置等待时间,按照上面的例子班长说等大家10分钟,如果10分钟后有士兵没到,就不继续等待,班长和其他士兵去修筑防御工事,注意这个时候班长就不讲话了,及时有这个安排,当迟到的士兵到了后就直接去去修筑防御工事。
public class CyclicBarrierDemo {
static CyclicBarrier c = new CyclicBarrier(2, new A());
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
try {
c.await(1,TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
System.out.println(1);
}
}).start();
try {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} c.await();
} catch (Exception e) {
}
System.out.println(2);
}
static class A implements Runnable {
public void run() {
System.out.println(3);
}
}
}
//这个例子中不输出 3
与CountDownLatch比较
CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:
- CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行。
-
CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。
-
CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。比如以下代码执行完之后会返回true。
Semaphore
简介
Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。
使用场景
访问数据库连接资源,有30个线程去访问数据库,但是数据库连接数只有10个,这时我们必须控制只有十个线程同时获取数据库连接保存数据,否则会报错无法获取数据库连接。
常用方法
-
void acquire():从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。
-
void release():释放一个许可,将其返回给信号量。
-
int availablePermits():返回此信号量中当前可用的许可数。
-
boolean hasQueuedThreads():查询是否有线程正在等待获取。
与Lock
单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。
Exchanger
简介
两个线程可以交换对象的同步点。每个线程都在进入 exchange 方法时给出某个对象,并接受其他线程返回时给出的对象。
使用场景
Exchanger也可以用于校对工作。比如我们需要将纸制银流通过人工的方式录入成电子银行流水,为了避免错误,采用AB岗两人进行录入,录入到Excel之后系统需要加载这两个Excel,并对这两个Excel数据进行校对,看看是否录入的一致。
常用方法
-
V exchange(V x) 等待另一个线程到达此交换点(除非它被中断),然后将给定的对象传送给该线程,并接收该线程的对象。
-
V exchange(V x, long timeout, TimeUnit unit) 等待另一个线程到达此交换点(除非它被中断,或者超出了指定的等待时间),然后将给定的对象传送给该线程,同时接收该线程的对象。