文章总结自B站狂神说JAVA
分支合并:并行执行任务来提高效率。把大任务划分成小任务来分来执行,将每个小任务得到的结果整合为最终结果。
特点:
demo:
/**
* @date 2020/7/28 7:54
*
* 求和计算任务
*/
public class ForkJoinDemo01 extends RecursiveTask<Long> {
private long start;
private long end;
// 临界值 超过分为两个任务
private long temp = 10000L;
public ForkJoinDemo01(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 middle = (start + end) / 2;
// 根据中间值,将计算任务分为 开始到中间和中间到结束。
ForkJoinDemo01 task1 = new ForkJoinDemo01(start, middle);
task1.fork(); // 拆分任务,把任务压入线程队列。
ForkJoinDemo01 task2 = new ForkJoinDemo01(middle+1, end);
task2.fork();
long result = task1.join() + task2.join();
return result;
}
}
}
测试类:
/**
* @date 2020/7/28 8:09
*
* 测试不同计算方法在数据较大时执行所需要的时间。
*/
public class Test01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// test1(); // 295
// test2(); // 227
// test3(); // 189
}
// for 循环计算
public static void test1(){
long sum = 0L;
long start = System.currentTimeMillis();
for (long i = 1L; i <= 10_0000_0000; i++) {
sum += i;
}
long end = System.currentTimeMillis();
System.out.println("sum="+sum+"执行时间:"+(end-start));
}
// fork join
public static void test2() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinDemo01(0L, 10_0000_0000L);
// 提交任务
ForkJoinTask<Long> submit = forkJoinPool.submit(task);
Long sum = submit.get();
long end = System.currentTimeMillis();
System.out.println("sum="+sum+"执行时间:"+(end-start));
}
// steam并行流
public static void test3(){
long start = System.currentTimeMillis();
long reduce = LongStream.range(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
long end = System.currentTimeMillis();
System.out.println("sum="+reduce+"执行时间:"+(end-start));
}
}
类似于AJAX,就是在Java的线程中使用异步调用。
/**
* @date 2020/7/28 9:23
* 异步异步回调
*/
public class Demo01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"void -> runAsync");
});
System.out.println("11");
// get 会阻塞(等待线程执行完毕)执行结果
Void aVoid = completableFuture.get();
}
}
/**
* @date 2020/7/28 9:36
* 有返回值的异步回调
*/
public class Demo02 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
// int i = 10/0;
System.out.println("supplyAsync -> run");
return 1;
});
System.out.println(completableFuture.whenComplete((t, u) -> {
System.out.println("t->" + t); // 正常的返回结果
System.out.println("u-|>" + u); // 错误的返回结果
}).exceptionally((e) -> {
// 发生错误的时候会执行。
e.getMessage();
return 1010;
}).get());
}
}
JMM:Java内存模型,是一种约定。
JMM同步的约定:
主内存与线程的工作内存之间有八种操作:
是Java虚拟机提供的轻量级的同步机制。
如果工作内存无法及时的获得主内存中已经修改了的值,那么就不会及时的做出决定。
/**
* @date 2020/7/28 10:00
* JMM Demo
*/
public class Demo01 {
// 主线程
private static int num = 0;
public static void main(String[] args) {
// 线程A
new Thread(()->{
while (num == 0){
}
}).start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
num = 1;
System.out.println(num);
}
}
如上方代码,即使等到main线程休眠结束后num变为1,线程A也不会结束,会一直死循环。
解决方案:使用volatile
关键字标识变量num
private volatile static int num = 0;
原子性:不可分割。线程A在执行任务时,不能被打扰,要么同时成功要么同时失败。
/**
* @date 2020/7/28 10:16
* 不保证原子性
*/
public class Demo02 {
private volatile static int num = 0;
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int i1 = 0; i1 < 1000; i1++) {
add();
}
}).start();
}
// 如果线程大于2线程礼让
while (Thread.activeCount()>2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+"--------->"+num);
}
public static void add(){
num++;
}
}
如何保证原子性:
lock
&synchornized'
加锁可以保证原子性,但是消耗的资源比较大。atomic
编译器优化会重排代码执行,指令并行也可能会重排,内存系统也会重排。处理器在执行指令重排的时候会考虑数据之间的依赖性。但是在多线程共同执行时,程序就不一定会出现应有的数值。添加volatile
可以避免指令重排。
避免原理:内存屏障: