JDK中提供了几个非常有用的并发工具类,也就是这次要讲的四大帅哥:CountDownLatch,CyclicBarrier,Semaphore,Exchanger。
import java.util.Random;
public class Test {
public static void main(String[] args) {
int nThreads = 10;
Thread[] threads = new Thread[nThreads];
for(int i = 0;i < nThreads;i++){
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("I am "+Thread.currentThread().getName()+"and starts");
try {
//模拟计算任务耗时
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I am "+Thread.currentThread().getName()+"and ends");
}
});
threads[i].start();
}
// 等待每个线程执行结束
for(int j = 0;j < nThreads;j++){
try {
threads[j].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 只有每个线程都执行结束了,这句话才会执行
System.out.println("ALL is over");
}
}
好了,join就到此为止吧,不能让主角等急了。下面就重点聊一下CountDownLatch。
public class TestHarness {
public long timeTasks(int nThreads, final Runnable task)
throws InterruptedException {
// 起始门
final CountDownLatch startGate = new CountDownLatch(1);
// 结束门
final CountDownLatch endGate = new CountDownLatch(nThreads);
for (int i = 0; i < nThreads; i++) {
Thread t = new Thread() {
public void run() {
try {
//在起始门处等待,知道开启起始门,才会往下执行
startGate.await();
try {
task.run();
} finally {
//执行完后,结束门计数器减1
endGate.countDown();
}
} catch (InterruptedException ignored) {
}
}
};
t.start();
}
long start = System.nanoTime();
//打开起始门,让所有线程开始执行
startGate.countDown();
//在结束门上等待,只有所有线程都执行完毕后才会继续往下执行
endGate.await();
long end = System.nanoTime();
return end - start;
}
}
如上,使用两个门:起始门,结束门。起始门的计数器初始值为1,而结束门计数器的初始值为工作者线程的数量。每一个工作者线程首先要做的事就是在起始门上等待,从而确保所有线程就绪后才开始执行。而每一个线程要做的最后一件事情就是调用结束门的counDown方法减1,这能使主线程高效地等待直到所有工作者线程都执行完成,因此可以统计所消耗的时间。
/**
* @param parties 屏障拦截的线程数量
*/
public CyclicBarrier(int parties){...}
/**
* @param parties 屏障拦截的线程数量
* @param barrierAction 所有线程到达屏障时优先执行barrierAction
*/
public CyclicBarrier(int parties, Runnable barrierAction){...}
对于每一个线程,当调用了await方法就是告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class BrankWaterService implements Runnable{
/**
* 穿件4个屏障,处理完之后执行当前类的run方法
*/
private CyclicBarrier c = new CyclicBarrier(4,this);
/**
* 假设只有4个sheet,所以启动4个线程
*/
private ExecutorService executor = Executors.newFixedThreadPool(4);
/**
* 保存每个sheet计算出来的银行流水线结果(使用ConcurrentHashMap保证线程安全)
*/
private Map sheetBankWaterCount = new ConcurrentHashMap();
private void count() {
for (int i = 0 ;i <4;i++){
executor.execute(new Runnable() {
@Override
public void run() {
// 计算当前sheet的银行流水线结果,计算过程忽视...
sheetBankWaterCount.put(Thread.currentThread().getName(), new Random().nextInt(10));
try {
//到达屏障
c.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
@Override
public void run() {
int result = 0;
//汇总每一个sheet计算的结果
for(Entry sheet : sheetBankWaterCount.entrySet()){
result += sheet.getValue();
}
System.out.println(result);
}
public static void main(String[] args) {
BrankWaterService brankWaterService = new BrankWaterService();
brankWaterService.count();
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemaphoreTest {
private static final int THREAD_cOUNT = 30;
private static ExecutorService pool = Executors.newFixedThreadPool(10);
private static Semaphore s = new Semaphore(10);
public static void main(String[] args) {
for(int i =0 ;i < THREAD_cOUNT;i++){
pool.execute(new Runnable() {
@Override
public void run() {
try {
s.acquire();
System.out.println("save data");
s.release();
} catch (InterruptedException e) {
}
}
});
}
pool.shutdown();
}
}
代码中虽然有30个线程,但是只允许10个并发执行。Semaphore的构造方法public Semaphore(int permits)接受一个整形的数字,表示可用的许可数量。 Semaphore(10)表示允许10个线程获取许可,那就是最大并发数是10。acquire方法获取一个许可,使用完之后使用release方法归还许可。Semaphore类中海油其他的一些方法可以使用,这里就不一一介绍了,大家有兴趣可以翻开一下JDK文档看一下或者直接看源代码。
import java.util.Random;
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExChangerTest {
public static void main(String[] args) {
Exchanger exchanger = new Exchanger();
int nThreads = 10;
ExecutorService pool = Executors.newFixedThreadPool(nThreads);
// 同时启动10个线程,每一个线程依次休息1,2,3,4...10秒,然后再交换数据
for (int i = 0; i < nThreads; i++) {
pool.execute(new Work(exchanger, (i + 1)));
}
pool.shutdown();
}
}
class Work implements Runnable {
// 同步器
private Exchanger exchanger;
// 休息的秒数
private int seconds;
public Work(Exchanger exchanger, int seconds) {
this.exchanger = exchanger;
this.seconds = seconds;
}
@Override
public void run() {
try {
Thread.sleep(seconds * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 从其他线程交换得来的的数据
Integer another = null;
// 本地产生的的数据
Integer localData = null;
//休息指定的时间后开始交换数据
try {
localData = new Random().nextInt(100);
another = exchanger.exchange(localData);//交换数据的同步点
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->>received data is:" + another);
System.out.println(Thread.currentThread().getName() + "-->>sended data is:" + localData);
}
}