引用 ifeve
work-stealing算法
工作窃取(work-stealing)算法是指某个线程从其他队列里窃取任务来执行。
那么为什么需要使用工作窃取算法呢?
假如我们需要做一个比较大的任务,我们可以把这个任务分割为若干互不依赖的子任务,为了减少线程间的竞争,于是把这些子任务分别放到不同的队列里,并为每个队列创建一个单独的线程来执行队列里的任务,线程和队列一一对应,比如A线程负责处理A队列里的任务。
但是有的线程会先把自己队列里的任务干完,而其他线程对应的队列里还有任务等待处理。干完活的线程与其等着,不如去帮其他线程干活,于是它就去其他线程的队列里窃取一个任务来执行
。而在这时它们会访问同一个队列,所以为了减少窃取任务线程和被窃取任务线程之间的竞争,通常会使用双端队列
,被窃取任务线程永远从双端队列的头部拿任务
执行,而窃取任务的线程永远从双端队列的尾部拿任务
执行。
工作窃取算法的优点是充分利用线程进行并行计算,并减少了线程间的竞争
当其它线程只存在一个任务时,工作窃取算法可能会存在竞争.
ForkJoinPool
JDK 1.7 时,标准类库添加了 ForkJoinPool
,作为对 Fork/Join 型线程池的实现。Fork 在英文中有 分叉
的意思,而Join
有 合并 的意思。
ForkJoinPool
的功能也是如此:Fork 将大任务分叉为多个小任务,然后让小任务执行,Join 是获得小任务的结果,然后进行合并,将合并的结果作为大任务的结果 —— 并且这会是一个递归的过程 —— 因为任务如果足够大,可以将任务多级分叉直到任务足够小。
使用Fork/Join框架,必须要了解两个类:
- ForkJoinTask: 我们要使用ForkJoin框架,必须首先创建一个ForkJoin任务。它提供在任务中执行
fork()和join()
操作的机制,通常情况下我们不需要直接继承ForkJoinTask类,而只需要继承它的子类,Fork/Join框架提供了以下两个子类:RecursiveAction
:用于没有返回结果
的任务。RecursiveTask
: 用于有返回结果
的任务。
public abstract class RecursiveAction extends ForkJoinTask<Void> {
}
public abstract class RecursiveTask<V> extends ForkJoinTask<V> {
}
- ForkJoinPool :ForkJoinTask需要通过ForkJoinPool来执行,任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当一个工作线程的队列里暂时没有任务时,它会随机从其他工作线程的队列的尾部获取一个任务。
**ForkJoinPool-API **
/**
* 执行一个带返回值的任务 --该方法是阻塞的,直到任务执行完毕,该方法才会停止阻塞并返回任务的执行结果。
* @param task 通常为 RecursiveTask类型
* @return 返回执行结果
*/
public <T> T invoke(ForkJoinTask<T> task) {}
/**
* 不带返回值的任务 --- 非阻塞的
*/
public void execute(ForkJoinTask<?> task) {}
/**
* 方法是---非阻塞的,调用之后将任务提交给 ForkJoinPool 去执行便立即返回
*
* ForkJoinTask实现了Future接口,通过 future.get()获取结果
*/
public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) {}
demo
需求:
计算 1+2+…+ 100 的结果。
如果我们希望每个子任务最多执行十个数的相加,那么我们设置分割的阈值是 10
code
public class M1_创建ForkJoin池 {
/**
* 需求是:计算 1+2+....+ 100 的结果。
* 如果我们希望每个子任务最多执行十个数的相加,那么我们设置分割的阈值是 10
*
* RecursiveAction:用于没有返回结果的任务。
* RecursiveTask :用于有返回结果的任务。
*/
static class MyCountRecursiveTask extends RecursiveTask<Integer> {
private static final int THRESHOLD = 10;
private int start ;
private int end;
public MyCountRecursiveTask(int start, int end) {
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
Integer sum = 0;
boolean canCal = (end - start) <= THRESHOLD;
if(canCal){
for (int i = start; i <= end ; i++) {
sum += i;
}
}else{
int middle = (start + end) / 2;
MyCountRecursiveTask leftTask = new MyCountRecursiveTask(start, middle);
MyCountRecursiveTask rightTask = new MyCountRecursiveTask(middle+1, end);
leftTask.fork(); //执行子任务left
rightTask.fork();//执行子任务right
int leftResult = leftTask.join();
int rightResult = rightTask.join();
sum = leftResult + rightResult;
}
return sum;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCountRecursiveTask task = new MyCountRecursiveTask(1, 10000);
ForkJoinPool pool = new ForkJoinPool();
Future future = pool.submit(task);
do {
System.out.println("********************************************************");
System.out.printf("Main: Pool Size: %d\n",pool.getPoolSize());
System.out.printf("Main: Active Thread Count: %d\n",pool.getActiveThreadCount());
System.out.printf("Main: Thread Steal: %d\n",pool.getStealCount());
System.out.printf("Main: Parallelism: %d\n",pool.getParallelism());
try {
TimeUnit.MILLISECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (!task.isDone());
System.out.println(future.get());
//如果出现异常,则显示异常信息
if(task.isCompletedAbnormally()){
System.out.println(task.getException());
}
}
}