一:Fork/Join 框架:
分而治之的思想:也就是将一项任务分为多个执行过程进行,最后将结果进行整合。在Linux通过fork()函数创建子进程,使得系统进程可以多一个执行分支(线程),要等待这个执行分支执行完毕,才有可能得到最终结果,而join()就表示等待。
由于线程池的优化,提交的任务和线程的数量并不是一对一的关系。大多数情况下,一个物理线程需要处理多个任务,因此,每个线程必然需要拥有一个任务队列。因此,很有可能线程A执行完所有任务后,线程B还有一堆任务等待处理,此时,线程A就会帮助B,从B的任务队列中拿一个任务过来处理,尽可能达到平衡。当线程帮助别人时,总是从任务队列的底部开始拿数据,而线程试图执行自己的任务时,则是从顶部开始拿,有利于避免数据竞争。
如果毫无顾忌的使用fork()开启线程进行处理,可能导致开启过多线程而严重影响性能。因此在JDK中,给出了一个ForkJoinPool线程池,对于fork()并不急着开启线程,而是提交给ForkJoinPool线程池进行处理,以节省系统资源。
1.2 ForkJoinPool的一个重要接口:
public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task)
你可以向ForkJoinPool线程池提交一个ForkJoinTask任务。所谓ForkJoinTask任务就是支持fork()分解以及join()等待的任务。ForkJoinTask有两个重要的子类,RecursiveAction和RecursiveTask.分别表示没有返回值的任务和可以携带返回值的任务
代码:
/**
* forkJoin框架案例
*/
public class ThreadUtils extends RecursiveTask<Long> {
private static final int THRESHOLD = 10000;
private long start;
private long end;
public ThreadUtils(long start,long end){
this.start = start;
this.end = end;
}
protected Long compute() {
long sum = 0;
boolean canCompute = (end -start)<THRESHOLD;
if (canCompute){
for (long i=start;i<=end;i++){
sum+=i;
}
}else {
//分成100个小任务
long step = (start+end)/1000;
ArrayList<ThreadUtils> subTasks = new ArrayList<>();
long pos = start;
for (int i=0;i<100;i++){
long lastOne = pos+step;
if (lastOne>end) lastOne=end;
ThreadUtils subTask = new ThreadUtils(pos,lastOne);
pos+=step+1;
subTasks.add(subTask);
subTask.fork();
}
for (ThreadUtils t : subTasks){
sum+=t.join();
}
}
return sum;
}
public static void main(String[] args){
ForkJoinPool forkJoinPool = new ForkJoinPool();
ThreadUtils task = new ThreadUtils(0,200000L);
ForkJoinTask<Long> result = forkJoinPool.submit(task);
try {
long res = result.get();
System.out.println("sum="+res);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
注意:分的任务大小不同,结果好像也不一样????
二:JDK并发容器:
构造线程安全的Map:使用代理模式,从原map中获取数据,其本身只是作为一个加锁的工具
public static Map map = Collections.synchronizedMap(new HashMap());
上述案例 返回 SynchronizedMap,而其实现方法就是使用mutex锁进行 synchronized锁定。方法都要获取锁操作,性能不高。