package mytest;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.TimeUnit;
public class ForkJoinTest {
/**
* 工作窃取(work-stealing)算法是指某个线程从其他队列里窃取任务来执行。那么,为什么需要使用工作窃取算法呢?假如我们需要做一个比较大的任务,可以把这个任务分割为若干互不依赖的子任务,为了减少线程间的竞争,把这些子任务分别放到不同的队列里,并为每个队列创建一个单独的线程来执行队列里的任务,线程和队列一一对应。比如A线程负责处理A队列里的任务。但是,有的线程会先把自
*
* 己队列里的任务干完,而其他线程对应的队列里还有任务等待处理。干完活的线程与其等着,不如去帮其他线程干活,于是它就去其他线程的队列里窃取一个任务来执行。而在这时它们会访问同一个队列,所以为了减少窃取任务线程和被窃取任务线程之间的竞争,通常会使用双端队列,被窃取任务线程永远从双端队列的头部拿任务执行,而窃取任务的线程永远从双端队列的尾部拿任务执行。
*
* ForkJoinTask与一般任务的主要区别在于它需要实现compute方法,在这个方法里,首先需要判断任务是否足够小,如果足够小就直接执行任务。如果不足够小,就必须分割成两个子任务,每个子任务在调用fork方法时,又会进入compute方法,看看当前子
*
* 任务是否需要继续分割成子任务,如果不需要继续分割,则执行当前子任务并返回结果。使用join方法会等待子任务执行完并得到其结果。
*
* 5050 cost time:40008 5050 cost time:50010
*
* @param
* @return
*/
public static void main(String[] args) throws InterruptedException, ExecutionException {
long start = System.currentTimeMillis();
ForkJoinPool pool = new ForkJoinPool();
List<Integer> numsList = new ArrayList<>();
for (int i = 1; i <= 100; i++) {
numsList.add(i);
}
Task task = new Task(numsList, 0, 100);
Future<Integer> submit = pool.submit(task);// ForkJoinTask时使用工作窃取算法,其他不使用,如submit(Callable<T>
// pool.execute(new Task(numsList, 0, 100)); 异步 // task)
System.out.println(submit.get());// 阻塞
System.out.println("cost time:" + (System.currentTimeMillis() - start));
pool.awaitTermination(1, TimeUnit.DAYS);// 等到线程结束
if (task.isCompletedAbnormally()) {
System.out.println("判断是否正常结束");
}
}
static class Task extends RecursiveTask<Integer> {// RecursiveAction不需要返回结果
Integer start;
Integer end;
List<Integer> numsList;
public Task(List<Integer> numsList, int start, int end) {
this.start = start;
this.end = end;
this.numsList = numsList;
}
@Override
protected Integer compute() {
int sum = 0;
if (end - start < 10) {
// completeExceptionally(new Exception());//可以指定异常
sum = calculate(numsList);
} else {
int mid = (end + start) / 2;// end+start
Task task1 = new Task(numsList, start, mid + 1);// mid+1
Task task2 = new Task(numsList, mid + 1, end);// mid+1
String name = Thread.currentThread().getName();
long start = System.currentTimeMillis();
/*
* 这种方式更快一点儿,不知为何!!!!
*
* invokeAll(task1, task2); try { sum = task1.get() +
* task2.get(); } catch (InterruptedException e) {
* e.printStackTrace(); } catch (ExecutionException e) {
* e.printStackTrace(); }
*/
ForkJoinTask<Integer> fork1 = task1.fork();
ForkJoinTask<Integer> fork2 = task2.fork();
start = System.currentTimeMillis();
Integer join1 = fork1.join();
Integer join2 = fork2.join();
sum = join1 + join2;
}
return sum;
}
private Integer calculate(List<Integer> numsList2) {
int result = 0;
for (int i = start; i < end; i++) {// <end
result += numsList2.get(i);
}
try {
Thread.sleep(1 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
}
}
/**
* 按路径查找指定扩展名,如果是文件夹,就fork一个新任务。最后join到集合中返回
*
*/
static class FolderProcessor extends RecursiveTask<List<String>> {
private static final long serialVersionUID = 1L;
private String path;
private String extension;
public FolderProcessor(String absolutePath, String extension) {
this.path = absolutePath;
this.extension = extension;
}
@Override
protected List<String> compute() {
List<String> list = new ArrayList<String>();
List<FolderProcessor> tasks = new ArrayList<FolderProcessor>();
File file = new File(path);
File content[] = file.listFiles();
if (content != null) {
for (int i = 0; i < content.length; i++) {
if (content[i].isDirectory()) {
FolderProcessor task = new FolderProcessor(content[i].getAbsolutePath(), extension);
task.fork();
tasks.add(task);
} else {
if (content[i].getName().endsWith(extension)) {
list.add(content[i].getAbsolutePath());
}
}
}
}
for (FolderProcessor item : tasks) {
list.addAll(item.join());
}
return list;
}
}
}
ForkJoin
最新推荐文章于 2024-06-11 21:50:02 发布