ForkJoin框架
ForkJoin是jdk1.7后发布的多线程并发处理框架,功能上和JUC类似,JUC更多的时候是使用单个类完成操作,ForkJoin使用多个类完成某项工作,处理上比juc更加丰富,实际开发中使用的场景并不是很多,互联网公司真正有高并发需求的时候才会使用,面试的时候会加分
它的本质上是对线程池的一种补充,对线程池的一种扩展,基于线程池的,它的核心思想就是将一个大型的任务拆分成很多个小任务,分别执行,最终将小任务的结果进行汇总,生成最终结果
本质上就是把一个线程的任务拆分成多个小任务,然后由多个线程并发执行,最终将结果进行汇总
比如A,B两个线程同时再执行,A的任务比较多,B的任务相对较少,B先执行完毕,这时候B去帮助A完成任务(将A的一部分任务拿过来替A执行,执行完毕之后再把结果进行汇总),从而提高效率,这也叫做工作窃取
ForkJoin框架,核心就两个类
- ForkJoinTask(描述任务)
- ForkJoinPool(线程池) 提供多线程并发工作窃取
使用forkjoinTask最重要的就是搞清楚如何拆分任务,这里用的是递归思想
1.需要创建一个ForkJoinTask任务,ForkJoinTask是一个抽象类,不能直接创建ForkJoinTask的实例化对象,开发者需要自定义一个类,继承ForkJoinTask的子类RecursiveTask,Recursive就是递归的意思,该类就提供了实现递归的功能
/**
* 10亿求和
*/
public class ForkJoinDemo extends RecursiveTask<Long> {
private Long start; // 开始数
private Long end; // 起始数
private Long temp = 100_0000L; // 临界值
public ForkJoinDemo(Long start, Long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if ((end-start)<temp){
Long sum = 0L;
for (Long i = start; i <=end ; i++) {
sum+=i;
}
return sum;
}else {
Long avg = (start+end)/2;
ForkJoinDemo task1 = new ForkJoinDemo(start, avg);
task1.fork();
ForkJoinDemo task2 = new ForkJoinDemo(avg, end);
task2.fork();
return task1.join()+task2.join();
}
}
}
测试代码
public class Test {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinDemo(0L, 10_0000_0000L);
// 执行
forkJoinPool.execute(task);
Long sum = 0L;
try {
sum = task.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
// 结束时间
long end = System.currentTimeMillis();
System.out.println(sum+"共耗时"+(end-startTime));
}
}
Volatile关键字
Volatile是jvm提供的轻量级同步机制,可见性,设置对主内存对线程可见
public class Test {
private static volatile int num = 0;
public static void main(String[] args) {
/**
* 循环
*/
new Thread(()->{
while (num==0){
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
num = 1;
System.out.println(num);
}
}
一个线程执行完任务之后,会把变量还回到主内存中,并且从主内存中读取到当前最新的值,如果是一个空的任务,则不会重新读取主内存中的值
public class Test {
private static int num = 0;
public static void main(String[] args) {
/**
* 循环
*/
new Thread(()->{
while (num==0){
System.out.println("Thread====>再执行");
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
num = 1;
System.out.println(num);
}
}