
目录
ForkJoin 框架
ForkJoin 框架,核心是两个类
Volatile 关键字
线程池 workQueue
目录
ForkJoin 框架
ForkJoin 框架,核心是两个类
Volatile 关键字
线程池 workQueue
递归
什么是递归?
递归需要满足 3 要素
ForkJoin 是 JDK 1.7 后发布的多线程并发处理框架,功能上和 JUC 类似,JUC 更多时候是使用单个类完成操作,ForkJoin 使用多个类同时完成某项工作,处理上比 JUC 更加丰富,实际开发中使用的场景并不是很多,互联网公司真正有高并发需求的情况才会使用,面试时候会加分
本质上是对线程池的一种的补充,对线程池功能的一种扩展,基于线程池的,它的核心思想就是将一个大型的任务拆分成很多个小任务,分别执行,最终将小任务的结果进行汇总,生成最终的结果。
本质就是把一个线程的任务拆分成多个小任务,然后由多个线程并发执行,最终将结果进行汇总。
比如 A B 两个线程同时还执行,A 的任务比较多,B 的任务相对较少,B 先执行完毕,这时候 B 去帮助 A 完成任务(将 A 的一部分任务拿过来替 A 执行,执行完毕之后再把结果进行汇总),从而提高效率。
工作窃取
ForkJoinTask (描述任务)
ForkJoinPool(线程池)提供多线程并发工作窃取
使用 ForkJoinTask 最重要的就是要搞清楚如何拆分任务,这里用的是递归思想。
1、需要创建一个 ForkJoinTask 任务,ForkJoinTask 是一个抽象类,不能直接创建 ForkJoinTask 的实例化对象,开发者需要自定义一个类,继承 ForkJoinTask 的子类 RecursiveTask ,Recursive 就是递归的意思,该类就提供了实现递归的功能。
import java.util.concurrent.RecursiveTask; public class ForkJoinDemo extends RecursiveTask{ 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) import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; public class Test { public static void main(String[] args) { Long startTime = System.currentTimeMillis(); ForkJoinPool forkJoinPool = new ForkJoinPool(); ForkJoinTasktask = 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 endTime = System.currentTimeMillis(); System.out.println(sum+",供耗时"+(endTime-startTime)); } } Volatile 关键字
Volatile 是 JVM 提供的轻量级同步机制,可见性,主内存对线程可见。
一个线程执行完任务之后,还会把变量存回到主内存中,并且从主内存中读取当前最新的值,如果是一个空的任务,则不会重新读取主内存中的值
import java.util.concurrent.TimeUnit; 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); } }import java.util.concurrent.TimeUnit; 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); } }线程池 workQueue
一个阻塞队列,用来存储等待执行的任务,常用的阻塞队列有以下几种:
ArrayBlockingQueue:基于数组的先进先出队列,创建时必须指定大小。
LinkedBlockingQueue:基于链表的先进先出队列,创建时可以不指定大小,默认值时 Integer.MAX_VALUE。
SynchronousQueue:它不会保存提交的任务,而是直接新建一个线程来执行新来的任务。
PriorityBlockingQueue:具有优先级的阻塞队列。
递归
二叉树遍历,深度优先搜索等。
什么是递归?
常规的定义:编程语言中,函数 func 直接或者间接调用函数本身,则该函数称为递归函数。
问前排人是第几排 -> 函数
所有的递归问题都可以用递推公式来表示,所以要用递归解决问题,关键就是先找到递推公式。
f(n) = f(n-1)+1
f(1) = 1
f(n) 表示你当前是第几排,f(n-1) 前面一排所在的排数,f(1) = 1 表示第一排的人知道自己是第一排。
int f(int n){ if(n == 1) return 1; return f(n-1)+1; }递归需要满足 3 要素
1、一个父问题可以拆分成若干个子问题,并且若干子问题的结果汇总起来就是父问题的答案。
2、父问题和子问题,解题思路必须完全一致,只是数据规模不同。
3、存在终止条件。
问题在不断拆分的同时,一定要在某个节点终止拆分,得到一个明确的答案。
问题:假设有 n 个台阶,每次可以跨 1 个台阶或者 2 个台阶,请问走完这 n 个台阶一共有多少种走法?
1、假设有 1 个台阶,一共有(1) 种走法
2、假设有 2 个台阶,一共有 2 种走法 【1,1】【2】
3、假设有 3 个台阶,一共有()种走法?【1,1,1】【1,2】【2,1】
......
可以根据第一步的走法进行分类
第一类是第一步走了 1 个台阶
第二类是第一步走了 2 个台阶
所以 n 个台阶的走法就等于先走 1 个台阶后,n-1 个台阶的走法+先走 2 个台阶后,n-2 个台阶的走法。
f(n) = f(n-1)+f(n-2)
f(1) = 1,能否作为终止条件?
n = 2,f(2) = f(1)+f(0),如果终止条件只有一个 f(1) = 1,f(2) 就无法求解, 因为 f(0) 的值无法确定,
把 f(2) = 2 作为一个终止条件
终止条件有两个:
f(1) = 1;
f(2) = 2;
n = 3,f(3) = f(2)+f(1) = 3
n = 4,f(4) = f(3)+f(2) = 3 + 2 = 5
递推公式
f(1) = 1;
f(2) = 2;
f(n) = f(n-1)+f(n-2);
推导出递归代码
int f(int n){ if(n == 1) return 1; if(n == 2) return 2; return f(n-1) + f(n-2); }public class Test { public static void main(String[] args) { for (int i = 1; i <= 30; i++) { System.out.println(i+"个台阶共有"+f(i)+"种走法"); } } public static int f(int n){ if(n == 1) return 1; if(n == 2) return 2; return f(n-1) + f(n-2); } }public class Test2 { public static void main(String[] args) { int d = f(10); System.out.println("d:"+d); } public static int f(int n){ if(n == 1){ System.out.println("m:"+n); return 1; }else{ System.out.println("n:"+n); //n=4 int c = f(n-1)+1; System.out.println("c:"+c); return c; } } }