CountDownLatch
类CountDownLatch是通过计数器的方式"组团"线程处理,然后回到主线程的一个辅助线程处理类。
通过设置要执行的线程数据,不为0时当前线程处于等待状态,直到设置的线程执行完。
使用方法,通过await方法锁定判断count数是否为0,通过countDown方法进行减1操作。
类方法:
用例:
public class CountDownLatchDemo extends Thread {
private CountDownLatch countDownLatch;
private int i;
public CountDownLatchDemo(CountDownLatch countDownLatch,int i){
this.countDownLatch = countDownLatch;
this.i = i;
}
@Override
public void run() {
System.out.println("开始执行线程"+i);
countDownLatch.countDown();
System.out.println("线程"+i+"执行完毕");
}
}
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(3);
CountDownLatchDemo downLatchDemo1 = new CountDownLatchDemo(countDownLatch,1);
downLatchDemo1.start();
CountDownLatchDemo downLatchDemo2 = new CountDownLatchDemo(countDownLatch,2);
downLatchDemo2.start();
CountDownLatchDemo downLatchDemo3 = new CountDownLatchDemo(countDownLatch,3);
downLatchDemo3.start();
countDownLatch.await();
System.out.println("主线程执行完毕");
}
执行结果:
开始执行线程1
线程1执行完毕
开始执行线程2
线程2执行完毕
开始执行线程3
线程3执行完毕
主线程执行完毕
备注:await方法可设时间,也可不设,如果设置时间,时间到期后会自动释放进入到主线程
CyclicBarrier
说明: 让一组线程到达某个屏障,然后被阻塞,一直到最后一个线程到达屏障,然后屏障开放,所有被阻塞的线程才能继续执行,计数器与线程数相等。
CyclicBarrier(int parties, Runnable barrierAction) 当时使用这个构造函数时,barrierAction定义的任务会执行
用例:
public class CyclicBarrierDemo extends Thread {
private CyclicBarrier cyclicBarrier;
private int i;
public CyclicBarrierDemo(CyclicBarrier cyclicBarrier,int i){
this.cyclicBarrier = cyclicBarrier;
this.i = i;
}
@Override
public void run() {
System.out.println("开始执行线程"+i);
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("线程"+i+"执行完毕");
}
}
public class CyclicBarrierAction extends Thread {
@Override
public void run(){
System.out.println("所有线程准备完毕后执行");
}
}
public static void main(String[] args) throws InterruptedException {
CyclicBarrier cyclicBarrier = new CyclicBarrier(3,new CyclicBarrierAction());
CyclicBarrierDemo cyclicBarrierDemo1 = new CyclicBarrierDemo(cyclicBarrier,1);
cyclicBarrierDemo1.start();
CyclicBarrierDemo cyclicBarrierDemo2 = new CyclicBarrierDemo(cyclicBarrier,2);
cyclicBarrierDemo2.start();
CyclicBarrierDemo cyclicBarrierDemo3 = new CyclicBarrierDemo(cyclicBarrier,3);
cyclicBarrierDemo3.start();
System.out.println("主线程执行完毕");
}
执行结果:
开始执行线程1
开始执行线程2
开始执行线程3
所有线程准备完毕后执行
线程3执行完毕
线程1执行完毕
线程2执行完毕
备注:
CountDownLatch和CyclicBarrier主要区别在于countDownLatch执行完后回到当前主线程,CyclicBarrier则回到指定线程
ForkJoin
ForkJoin框架的本质是一个用于并行执行任务的框架, 能够把一个大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务的计算结果。
典型应用
二分搜索
大整数乘法
Strassen矩阵乘法
棋盘覆盖
合并排序
快速排序
线性时间选择
汉诺塔
ForkJoin框架的实现
ForkJoin框架中一些重要的类如下所示。
ForkJoinPool 框架中涉及的主要类如下所示。
1.ForkJoinPool类
实现了ForkJoin框架中的线程池,由类图可以看出,ForkJoinPool类实现了线程池的Executor接口。
我们也可以从下图中看出ForkJoinPool的类图关系。
其中,可以使用Executors.newWorkStealPool()方法创建ForkJoinPool。
ForkJoinPool中提供了如下提交任务的方法。
public void execute(ForkJoinTask<?> task)
public void execute(Runnable task)
public <T> T invoke(ForkJoinTask<T> task)
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task)
public <T> ForkJoinTask<T> submit(Callable<T> task)
public <T> ForkJoinTask<T> submit(Runnable task, T result)
public ForkJoinTask<?> submit(Runnable task)
2.ForkJoinWorkerThread类
实现ForkJoin框架中的线程。
3.ForkJoinTask<V>类
ForkJoinTask封装了数据及其相应的计算,并且支持细粒度的数据并行。ForkJoinTask比线程要轻量,ForkJoinPool中少量工作线程能够运行大量的ForkJoinTask。
ForkJoinTask类中主要包括两个方法fork()和join(),分别实现任务的分拆与合并。
fork()方法类似于Thread.start(),但是它并不立即执行任务,而是将任务放入工作队列中。跟Thread.join()方法不同,ForkJoinTask的join()方法并不简单的阻塞线程,而是利用工作线程运行其他任务,当一个工作线程中调用join(),它将处理其他任务,直到注意到目标子任务已经完成。
我们可以使用下图来表示这个过程。
ForkJoinTask有3个子类:
RecursiveAction:无返回值的任务。
RecursiveTask:有返回值的任务。
CountedCompleter:完成任务后将触发其他任务。
4.RecursiveTask<V> 类
有返回结果的ForkJoinTask实现Callable。
5.RecursiveAction类
无返回结果的ForkJoinTask实现Runnable。
6.CountedCompleter<T> 类
在任务完成执行后会触发执行一个自定义的钩子函数。
用例:
public class SumArray {
//产生一个随机数组
private static int[] array = MakeArray.makeArray();
//阈值
private final static int POINT = MakeArray.ARRAY_LENGTH / 10;
//有返回值的任务
private static class SumTask extends RecursiveTask<Integer> {
//要累加的源数组
private int[] src;
//开始角标
private int startIndex;
//结束角标
private int endIndex;
public SumTask(int[] src, int startIndex, int endIndex) {
this.src = src;
this.startIndex = startIndex;
this.endIndex = endIndex;
}
//实现具体的累加逻辑和任务分割逻辑
@Override
protected Integer compute() {
//不满足阈值的时候,这里面的逻辑也是当满足阈值的时候,递归执行的逻辑
if (endIndex - startIndex < POINT) {
int count = 0;
for (int i = startIndex; i <= endIndex; i++) {
count += src[i];
// SleepTools.ms(1);
}
return count;
//满足阈值的时候,需要分割任务,然后交给forkjoinpool去执行任务
} else {
//当需要分割的时候,采用折中法进行分割
//startIndex.......mid.......endIndex
int mid = (startIndex + endIndex) / 2;
//左任务
SumTask leftTask = new SumTask(src, startIndex, mid);
//右任务
SumTask rigthTask = new SumTask(src, mid + 1, endIndex);
//交给forkjoinpool去执行任务
invokeAll(leftTask, rigthTask);
//将执行结果返回
return leftTask.join() + rigthTask.join();
}
}
}
private static void testForkJoin() {
//创建ForkJoinPool池
ForkJoinPool forkJoinPool = new ForkJoinPool();
long startTime = System.currentTimeMillis();
SumTask task = new SumTask(array, 0, array.length - 1);
//这个方法是阻塞的,是同步的
forkJoinPool.invoke(task);
long endTime = System.currentTimeMillis();
System.out.println("采用forkjoin执行结果是:" + task.join() + "---------用时:" + (endTime - startTime));
}
private static void testFor() {
long startTime = System.currentTimeMillis();
int count = 0;
for (int i = 0; i < array.length; i++) {
count += array[i];
// SleepTools.ms(1);
}
long endTime = System.currentTimeMillis();
System.out.println("采用for循环执行结果是:" + count + "---------用时:" + (endTime - startTime));
}
public static void main(String[] args) {
testForkJoin();
testFor();
}
}