美文网首页
并发补充

并发补充

作者: 蓝调_4f2b | 来源:发表于2023-06-15 09:37 被阅读0次

1. 并发编程

1.1 ForkJoin处理线程池任务

(1)任务类型:
CPU密集型:CPU数 = 核心线程数
I/O密集型: 核心线程数 = CPU * (1 + 平均等待时间/平均工作时间)
(2)思考:如何充分利用多核CPU资源,计数一个很大的数组之和?
方案1:利用多线程拆分任务,hash后采用分治 + 分段相加方式累加计数
注1:线程一定越多越好吗?不一定,CPU核数一定,线程数设定越多越多次数进行上下文切换,大大降低效率
方案2:forkJoin思想

if ((hi - lo) <= seq - cutoff) {
  for (i = lo; i < hi; i++) {
    result += arr[i];
  } else {
    RecursiveSumTask = new RecursiveSumTask(executorSevice, arr, lo, (hi + lo) / 2);
    RecursiveSumTask = new RecursiveSumTask(executorSevice, arr, (hi + lo) / 2, hi);
    Future<long> lr = executorService.submit(left);
    Future<long> rr = executorService.submit(right);
    result = lr + rr;
  }
} 

注1:小问题:该模式下,若采用定长线程池,在拆解任务过程中,可能由于线程资源不够,造成死锁
注2:应用场景:java并发包中提供的fork/join并行计算框架,用来支持分治任务模型

1.2 ForkJoin并发框架

传统线程池ThrealPool的缺点:(1)无法对大任务进行拆分 (2)工作线程从队列中获取任务时存在竞争
(1)特性:
允许其他线程向其提交任务,并将一个大任务拆分为多个
同时工作线程间支持任务窃取
(2)使用场景:处理计算密集型任务
(3)工作原理:


工作原理.png
  • 多个线程,每个维护一个工作队列workQueue,存入workQueues[]中
  • 提交任务,无工作Thread则创建,有工作Thread则唤醒并执行
  • 分配任务:自己取pop;任务窃取poll
    注:工作窃取优点:重复利用闲置线程进行并行计算;减少线程竞争

2. Future及CompleteableFuture

2.1 创建线程的几种方式及区别

2.2 Callable & Future

(1)直接继承Thread或实现Runnable接口都可创建线程,但存在无返回值的缺点;
(2)使用过程

FutureTask task = new FutureTask(new Callable() {
  public Object call() throws Exception {
     Thread.sleep(2000);
      return result;
  }
});
使用过程图.png
(3)使用场景:优化串行流程
优化串行流程.png
(4)使用局限性:
无法组合任务;无法对多个任务串行调用;有一个FutureTask任务阻塞就全部阻塞
(5)优化方式:CompletionService
通过阻塞队列 + FutureTask实现
已完成对任务存入阻塞队列中,用队列中的take方法可调用完成的result,总结果由get()获取
(6)思考Callable与Future会产生新线程吗?
不会,只是一种管理任务的方式,java中仅有new Thread一种生成新线程方式
(7)completeFuture:是Future扩展接口,实现了对任务的编排功能
补充:

相关文章

网友评论

      本文标题:并发补充

      本文链接:https://www.haomeiwen.com/subject/zmgkydtx.html