Fork-Join
什么是分而治之
规模为N的问题,N<阈值,直接解决,N>阈值,将N分解成K个小规模问题,子问题互相独立,与原问题形式相同,将子问题的解合并得到原问题的解
动态规范:规模为N的问题,N<阈值,直接解决,N>阈值,将N分解成K个小规模问题,子问题互相有联系,与原问题形式相同,将子问题的解合并得到原问题的解。
流程图
RecursiveTask:指定任务有返回值的时候继承这个类
RecursiveAction:指定任务没有返回值的的时候继承这个类
使用时需覆盖两个类中的compute方法
Fork-Join的同步用法
package com.day03;
import java.util.Random;
/**
* 产生整型数据
*/
public class MakeArray {
//数组长度
public static final int ARRAY_LENGTH = 4000;
public static int [] makeArray(){
//new一个随机数发生器
Random r = new Random();
int [] result = new int[ARRAY_LENGTH];
for (int i = 0; i <ARRAY_LENGTH ; i++) {
//用随机数填充数组
result[i] = r.nextInt(ARRAY_LENGTH*3);
}
return result;
}
}
package com.day03;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
public class SumArray {
private static class SumTask extends RecursiveTask<Integer>{
private final static int THRESHOLD = MakeArray.ARRAY_LENGTH/10;
private int[] src;//表示我们要实际统计的数组
private int fromIndex;//开始统计的下标
private int toIndex;//统计到哪里结束的下标
public SumTask(int[] src, int fromIndex, int toIndex) {
this.src = src;
this.fromIndex = fromIndex;
this.toIndex = toIndex;
}
@Override
protected Integer compute() {
if (toIndex-fromIndex<THRESHOLD){
int count = 0;
for (int i = fromIndex; i < toIndex; i++) {
try {
Thread.sleep(100);
count = count+src[i];
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return count;
}else {
int mid = (fromIndex+toIndex)/2;
SumTask left = new SumTask(src,fromIndex,mid);
SumTask right = new SumTask(src,mid+1,toIndex);
invokeAll(left,right);
return left.join() + right.join();
}
}
}
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool();
int [] src = MakeArray.makeArray();
SumTask innerFind = new SumTask(src,0,src.length);
long start = System.currentTimeMillis();
pool.invoke(innerFind);//同步调用
System.out.println("值:"+innerFind.join()+"时间:"+(System.currentTimeMillis()-start)+"ms");
}
}
总结
有时候使用多线程未必比单线程快。就好比上面代码进行简单的累加运算。在总数比较小时,单线程就比较快。多线程比较慢,原因是开启了个多线程,线程之间进行切换(时间片轮转机制)消耗了时间。所以选择单线程还是多线程,需要具体的任务详情。
Fork-Join的异步用法
package com.day03;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
/**
* 遍历指定目录(含子目录)找寻指定类型文件
*/
public class FindDirsFiles extends RecursiveAction {
private File path;
public FindDirsFiles(File path) {
this.path = path;
}
public static void main(String[] args) {
try {
//用一个ForkJoinPool实现调度总任务
ForkJoinPool pool = new ForkJoinPool();
FindDirsFiles task = new FindDirsFiles(new File("F:/"));
pool.execute(task); //异步调用
System.out.println("Task is Running...");
Thread.sleep(1);
int otherWork = 0;
for (int i = 0; i <100 ; i++) {
otherWork = otherWork + i;
}
System.out.println("Main Thread done sth....,otherWork="+otherWork);
task.join();//阻塞主线程,让ForkJoinPool进行工作
System.out.println("Task end");
}catch (Exception e){
e.printStackTrace();
}
}
@Override
protected void compute() {
List<FindDirsFiles> subTasks = new ArrayList<>();
File [] files = path.listFiles();
if (files!=null){
for (File file:files) {
if (file.isDirectory()){
subTasks.add(new FindDirsFiles(file));
}else{
//遇到文件,检查
if(file.getAbsolutePath().endsWith("txt")){
System.out.println("文件:"+file.getAbsolutePath());
}
}
}
if(!subTasks.isEmpty()){
for (FindDirsFiles subTask: invokeAll(subTasks)) {
subTask.join();//等待所有子任务完成
}
}
}
}
}
常用的并发工具类
CountDownLatch
作用:是一个线程等待其他线程完成工作后再执行的工具类。加强版join。、
await 方法用来等待,countDown负责计数器减一
CyclicBarrier
让一组线程达到某个屏障,被阻塞,一直到组内最后一个和线程达到屏障时,屏障开放,所有被阻塞的线程会继续运行
CyclicBarrier(int parties,Runnable barrierAction)当屏障开放时,barrierAction里面定义的任务会被执行
CountDownLatch和CyclicBarrier的区别。
1.CountDownLatch是由外部线程控制的决定是否继续运行,CyclicBarrier是由内部自己的线程决定的,只有当自己内部的线程都达到一个屏障时才会放行。
2.CountDownLatch放行让条件>=线程数,CyclicBarrier放行条件=线程数。
Semaphore
控制同时访问某个特定资源的线程数量,用在流量控制
Exchange
两个线程间的数据交换,里面的exchange()方法时阻塞方法,当一个线程先执行到exchange()方法时,会进行等待。等待另一个线程达到exchange()方法
Callable和Runnable区别
Callable 有返回值,能抛出异常。Runnable则不行
Callable、Future和FutureTask
FutureTask类实现了RunnableFuture接口,能够交给线程跑,里面接收的参数是Callable的实现,能够有返回值。Future接口里面的get()方法阻塞方法,会一直等待Callable运行完,返回结果。get()方法也有一个时间超时的设定。
isDone(),结束,正常还是异常结束,或者自己取消。都返回true。若是还在运行就返回false。
isCancelled() 任务完成前被取消,返回true;
cancel(boolean x):尝试结束任务,参数X:是否对运行中的任务进行中断。
1、任务还没开始,返回false
2、任务已经启动,cancel(true),尝试中断正在运行的任务。中断成功返回true。cancel(false)不会去中断已经运行的任务
3、任务已经结束,返回false