饥饿情况
-
如果每次,对于每一个任务都分配一个pool.invoke(new Task)这种,就会由于线程的数量不够,而导致饥饿。就是所有线程都处于waiting状态。
-
不知道为什么使用task.fork()创建新的线程,为什么不会出现饥饿的情况。
因为正常情况下。如果线程数目太少。就没法递归到最后一个任务,从而出现前面的线程处于waiting状态。但是现在却没有新的线程处理需要计算的这个任务。导致所有的线程处于饥饿的状态。 -
如果需要解决这种情况,应该使用三类线程,一类进行专门的任务分类,一类进行计算,最后一类进行任务的统计。估计应该是这么设计的。
-
也可以是两类线程。一类进行分类和计算,一类进行计算。或者把统计和计算的线程做成一类。
结果比较
由于设置的thread是1e5,比较大,所以手动的时间是比parrallel的时间快上不少的。
但是实际上。如果数据的范围小于1e7,会发现,还是使用单线程的最快。
这是由于
- 使用forkJoin会创建Task类。
- 存在递归调用,有栈上下文的切换。
- 会创建上下文,有线程上下文的切换。
详细代码
package threadBase.threadPool;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.stream.LongStream;
class Task extends RecursiveTask <Long>{
int l, r;
int thread;
public Task(int l, int r, int thread) {
this.l = l;
this.r = r;
this.thread = thread;
}
@Override
protected Long compute() {
if (r - l + 1 <= thread) {
long ans = 0;
for (int i = l; i <= r; i++)
ans += i;
return ans;
}
int mid = l + r >> 1;
Task t1 = new Task(l, mid, thread);
Task t2 = new Task(mid + 1, r, thread);
t1.fork();
t2.fork();
return t1.join() + t2.join();
}
}
public class ForkjionTest {
/*
使用函数进行所谓的任务计算,
不用类作为计算任务,
直接使用lambda表达式,然后直接使用函数作为任务内容进行计算就行了。
不是函数式接口,上面的设想不成立。服了
但是可以写成内部函数。
直接手动fork,手动join。
不过这样好像就只有一个线程?
*/
long testForkJoinInFunction(int l, int r, ForkJoinPool pool) {
if (l == r) return l;
int mid = l + r >> 1;
long ans = pool.invoke(new RecursiveTask<Long>() {
@Override
protected Long compute() {
long ans1 = testForkJoinInFunction(l, mid, pool); // 手动fork
long ans2 = testForkJoinInFunction(mid + 1, r, pool);
return ans1 + ans2; // 手动join
}
});
// System.out.println("线程:" + Thread.currentThread());
return ans;
}
public long testClass(int l,int r, ForkJoinPool pool) {
Task task = new Task(l, r, (int)1e5);
return pool.invoke(task);
}
public long testParralle(int l, int r) {
long reduce = LongStream.rangeClosed(l, r).parallel().reduce(0, (x, y) -> x + y);
return reduce;
}
public static void main(String[] args) {
ForkjionTest fp = new ForkjionTest();
ForkJoinPool pool = new ForkJoinPool(16);
int n = (int)1e9;
long startTime = System.currentTimeMillis();
long ans = fp.testClass(0, n, pool);
long endTime = System.currentTimeMillis();
System.out.println("ans = " + ans);
System.out.println("手动Fork-Join时间为:" + (endTime - startTime) + "ms");
ans = 0;
startTime = System.currentTimeMillis();
for (int i = 0; i <= n; i++) {
ans += i;
}
endTime = System.currentTimeMillis();
System.out.println("ans = " + ans);
System.out.println("单线程时间为:" + (endTime - startTime) + "ms");
startTime = System.currentTimeMillis();
ans = fp.testParralle(0, n);
endTime = System.currentTimeMillis();
System.out.println("ans = " + ans);
System.out.println("Parrallel运行时间:" + (endTime - startTime) + "ms");
}
}