算法思想–>Fork/Join–分而治之
大问题—>分割成小问题, 小问题之间无关联 -->分而治之
A, a1,a2,a3,a4–>a1(a3) , a2(a4) , 问题之间有依赖, -->动态规划
forkJoin中的工作密取:当同时存在多个任务线程,有线程先完成,那么先完成的线程从未完成的任务列表尾部取一部分处理,处理完成后再放回去
ForkJoin,如何使用
- RecursiveTask(递归任务)/RecursiveAction/ForkJoinTask
任务拆分到最小后处理完,再以Join方法合并结果,没返回值的用(Action),有返回值的用(Task) - pool 同步提交 invoke
异步提交 submit, execute
实例:统计一个大数组中的数字之和----同步返回结果
//创建一个大数组
int[] bigArr = {...........};
//创建ForkJoin任务池
ForkJoinPool pool = new ForkJoinPool();
//创建任务实例
SumTask task = new SumTask(bigArr,0,bigArr.length-1);
pool.invoke(task);
//结果
Integer result = task.join();
SumTask类
public class SumTask extends RecursiveTask<Ingeger>{
//阈值
private final static int THRESHOLD = 100;
private int[] src;
private int fromIndex,toIndex;
public SumTask(int[] src,int fromIndex,int toIndex){
this.src = src;
this.fromIndex = fromIndex;
this.toIndex = toIndex;
}
@Override
protected Integer compute(){
//判断任务大小是否合适
if(toIndex-fromIndex<THRESHOLD){//大小合适
int count=0;
for(int i=0;i<src.length;i++){
count +=src{i};
}
return count;
}else{//切分任务
int mid = (fromIndex+toIndex)/2;
SumTask ta1 = new SumTask(src,fromIndex,mid);
SumTask ta2 = new SumTask(src,mid+1,toIndex);
invokeAll(ta1,ta2);
return ta1.join()+ta2.join();
}
}
}
实例:归并排序----同步返回结果
public static class RecursiveSortTask extends RecursiveTask<Integer[]>{
private int threshold = 100;//任务组大小阈值
private int startIndex = 0;
private int endIndex = 0;
private Integer[] src;
public RecursiveSortTask(Integer[] src,int start,int end) {
this.src = src;
this.startIndex = start;
this.endIndex = end;
}
@Override
protected Integer[] compute() {
if(endIndex-startIndex<=threshold) {//不满足阈值的,直接排序
Integer[] _temp = Arrays.copyOfRange(src, startIndex, endIndex+1);
if(_temp.length<2) {
return _temp;
}
//倒序
for(int i=0;i<_temp.length-1;i++) {
for(int j=0;j<_temp.length-1-i;j++) {
if(_temp[j]<_temp[j+1]) {
int temp = _temp[j];
_temp[j]=_temp[j+1];
_temp[j+1]=temp;
}
}
}
//System.out.println(startIndex+"--"+endIndex+"-->"+String.join(",",Arrays.asList(_temp).stream().map(t->t+"").collect(Collectors.toList())));
return _temp;
}else {
int mid = (startIndex+endIndex)/2;
RecursiveSortTask ta1 = new RecursiveSortTask(src, startIndex, mid);
RecursiveSortTask ta2 = new RecursiveSortTask(src, mid+1,endIndex);
invokeAll(ta1,ta2);
//合并结果集
Integer[] _temp1 = ta1.join();
Integer[] _temp2 = ta2.join();
int totalCount = _temp1.length+_temp2.length;
Integer[] _result = new Integer[totalCount];
int a1 =0,a2=0;
for(int i=0;i<totalCount;i++) {
if(a1==_temp1.length) {//表示1数组已取完
_result[i] = _temp2[a2];
a2++;
continue;
}
if(a2==_temp2.length) {//表示2数组已取完
_result[i] = _temp1[a1];
a1++;
continue;
}
if(_temp1[a1]>=_temp2[a2]) {
_result[i] = _temp1[a1];
a1++;
}else {
_result[i] = _temp2[a2];
a2++;
}
}
return _result;
}
}
}
public static void main(String[] args) {
int count = 100000;
Integer[] _temp = new Integer[count];
for(int i =0;i<count;i++) {
_temp[i] = Double.valueOf(Math.random()*count).intValue();
}
ForkJoinPool pool = new ForkJoinPool();
RecursiveSortTask task = new RecursiveSortTask(_temp, 0, _temp.length-1);
pool.invoke(task);
pool.invoke(task);
_temp = task.join();
System.out.println(String.join(",",Arrays.asList(_temp).stream().map(t->t+"").collect(Collectors.toList())));
}
实例:遍历目录找文件–异步返回结果
StampedLock 高性能读写锁
乐观读锁:
获取读锁时不加锁,直接返回一个值,执行临界区的时候去验证这个值是否被修改(写锁加锁)
如果没被修改,则直接执行临界区代码,如果被修改则升级为读写锁(ReentrantReadWriteLock-->ReadLock)
StampedLock lock = new StampedLock();
long stamp = lock.tryOptimisticRead();
if(lock.validate(stamp)){//验戳
....
return ;
}
//验证失败
try{//锁升级
stamp = lock.readLock();
...业务代码
return ;
}finally{
lock.unlockWrite(stamp);
}
CountDownLatch 类,闭锁—底层AQS
用处:相当于发令枪一样,等一定数量线程都执行完一定初始化工作,然后调用await()之后, 此一定数量的线程同时唤醒, 也可以调用countDown()方法减少计数到0,也会唤醒线程
CountDownLatch latch = new CountDownLatch(5);
latch.countDown(); //手动减少计数
latch.await(); //线程等待并减少计数
CyclicBarrier 类 循环屏障
- 等所有子线程都await()之后, 先执行构造中的Runnable(barrierAction)汇总任务以合并子线程处理结果,然后所有子线程才能继续往下执行
- 体现出循环性, 在所有子线程都被唤醒后, 子线程可以继续调用await(),然后等待下一次唤醒并处理
Semaphore 信号量类 --底层AQS
3. 流量控制— acquire()方法获取执行许可(执行许可有限)—>release()释放执行许可
4. 注意事项:不使用acquire(),直接调用release(),会直接增加许可证数量
Exchanger 类:用于两个线程的协作–只支持两个线程
两个线程都调用exchange()方法, 互相交换数据,该方法会阻塞线程
Callable和FutureTask:解决普通线程执行没有返回值的问题
- FutureTask 实现的接口, 他可以作为一个runnable在Thread里直接使用, 又实现了Future接口, 他可以get()结果, 这块看看源码就知道
XXXCallable _temp = new XXXCallable();
FutureTask<xxx> task = new FutureTask<xxx>(_temp);
new Thread(task).start();
//获取结果
task.get(); //会在结果获取之前阻塞线程
//中断任务需要在callable里响应interrupt标志位, 同时调用task的取消方法
task.cancel(true); //本质就是interrupt() 方法来中断线程