当前位置: 首页 > 后端技术 > Java

Java多线程学习笔记(6)长乐未央篇

时间:2023-04-01 23:27:55 Java

突然发现多线程系列的话题都跑完了:初遇,相识,大喜,久乐不厌,长乐无极,长乐未央。算算我自己的多线程相关文章:《当我们说起多线程与高并发时》《Java多线程学习笔记(一) 初遇篇》《Java多线程学习笔记(二) 相识篇》《Java多线程学习笔记(三) 甚欢篇》《Java多线程学习笔记(四) 久处不厌》《Java多线程学习笔记(五) 长乐无极》《 ThreadLocal学习笔记》今天应该是多线程学习笔记的最后一章了。在这篇文章中,应该会用到JDK中多线程的基本概念。还没有引入并发集合和并行流。我会重点介绍并发集合的实现,也就是上面文章介绍的一些基础类的原理,因为并发集合的使用和普通集合没有太大的区别,这也是下一个系列的文章,也就是源码系列文章,去年10月份开始的《当我们说起看源码时,我们是在看什么》,是时候填这个坑了,并行流还是放在了Stream系列文章中。这些文章目前大概在掘金和ThinkNo,并不统一。有空的时候把三个平台的文章统一在下面。如果你在公众号上找到了以上文章,那么迁移就大致完成了。ForkJoin模式介绍本文的主角是ForkJoin,作者是DougLea的作品。在看ForkJoinPool的时候,心血来潮想看看其他并发类的作者,然后发现下面没有包含未被发现的CountDownLatchCyclicBarrierSemaphoreThreadPoolExecutorFutureCallableConcurrentHashMapCopyOnWriteArrayList好像是JDK中的并发类库是这位老哥创建的.在看这位老哥的作品的时候,我也发现自己漏了类:Phaser,所以会另开一篇,下一个多线程系列会把Phaser补回来。.说了这么多,接下来我们来介绍一下ForkJoin。在IDEA中使用ForkJoin全局搜索,结果如下:先来看ForkJoinPool的继承类图:这里可以看到ForkJoinPool和ThreadPoolExecutor是同级的,ThreadPoolExecutor是一个线程池。我们对此很熟悉,所以我们可以推测ForkJoinPool是另一种类型的线程池。那么这种ForkJoinPool和ThreadPoolExecutor有什么区别呢?带着这个疑问,我们来看一下ForkJoinPool的注释。注意,由于ForkJoinPool和ThreadPoolExecutor都属于ExecutorService的子类,所以ForkJoinPool的注释不会说ThreadPoolExecutor是另一种线程池(ThreadPool,而是另一种形式的ExecutorService)。用于运行ForkJoinTasks的ExecutorService。ForkJoinPool为来自非ForkJoinTask客户端的提交以及管理和监视操作提供入口点。ForkJoinPool与其他类型的ExecutorService的不同之处主要在于采用工作窃取:池中的所有线程都尝试查找和执行任务提交到池和/或由其他活动任务创建(如果不存在,最终阻塞等待工作)。当大多数任务产生其他子任务(大多数ForkJoinTasks也是如此)时,以及当许多小任务从外部客户端提交到池时,这可以实现高效处理。特别是在构造函数中将asyncMode设置为true时,ForkJoinPools也可能适用于从未加入的事件式任务。所有工作线程都初始化为Thread.isDaemon设置为true.ForkJoinPool这个异常执行服务(或者译为这个ExecutorService执行一些ForkJoinTasks)执行的是ForkJoinTask(Fork:分叉、崎开两条分割)以是唯一,kJoinTask可以理解为拆分合并任务,也可以执行一些不属于该类型的任务。管理和监控操作ForkJoinPool与其他类型的ExecutorService的主要区别在于它采用了工作窃取算法:线程池中的所有线程都会尝试查找并执行提交给该线程的任务和其他未完成的任务(如果没有任务,所有线程都将处于阻塞等待状态)。这种处理方式对于一些可以将大任务切分成子任务,与其他客户端一起提交到线程池的小任务(即ForkJoin任务),这种处理方式非常高效。ForkJoinPool也可能更适合永远不需要合并结果的事件驱动型任务。所有工作线程在初始化期间都被设置为守护线程。work-stealing算法介绍在介绍work-stealing算法之前,我们先回顾一下ThreadPoolExecutor的工作模式。当客户端向线程池提交任务时,线程池会先判断当前工作线程是否小于核心线程数。如果小于核心线程数,会继续向线程添加工作线程,如果不小于核心线程数,任务会被放入任务队列,如果队列已满,则判断当前工作线程是否大于最大线程数,如果大于等于最大线程数将触发拒绝策略。//ThreadPoolExecutor的执行方法publicvoidexecute(Runnablecommand){if(command==null)thrownewNullPointerException();intc=ctl.get();//ctl可以简单理解为线程的状态数量//workerCountOf用于计算工作线程数if(workerCountOf(c)saturate,longkeepAliveTime,TimeUnitunit)//自JDK9引入1.2.3本质调用4.重点说说4.parallelism并行粒度,就是设置线程池的线程数。使用无参构造,即Runtime.getRuntime().availableProcessors()的返回值。此方法返回逻辑核心而不是物理核心。我的电脑有八核和十六核,所以得到的数字是16factorythread项目。添加工作线程时,调用该方法添加工作线程处理程序异常处理程序。如何处理遇到问题的工作线程。asyncMode用于控制消费模式,如果为true,则为从未加入的forked任务建立本地先进先出调度模式。在工作线程仅处理事件式异步任务的应用程序中,此模式可能比默认的基于本地堆栈的模式更合适。对于默认值,请使用false。如果为true,Fork任务将以先进先出的模式进行调度,并且永远不会合并任何这些任务。这种调度方式比较适合一些本地基于栈的应用,这些工??作线程处理事件驱动的异步任务。默认为假。workerNamePrefix:用于控制工作线程的名称。接下来我们看看如何向ForkJoinPool提交任务。我们看到一个新的参数类型:ForkJoinTask。下面以一个例子来介绍ForkJoin.publicclassFibonacciForkJoinTaskextendsRecursiveTask{privatefinalintn;publicFibonacciForkJoinTask(intn){this.n=n;}@OverrideprotectedIntegercompute(){if(n<=1){returnn;}FibonacciForkJoinTaskf1=newFibonacciForkJoinTask(n-1);f1.fork();//分解f1FibonacciForkJoinTaskf2=newFibonacciForkJoinTask(n-2);f2.fork();//分解f2returnf1.join()+f2.join();//合并f1和f2的计算结果}}publicclassForkJoinDemo01{publicstaticvoidmain(String[]args)throwsException{ForkJoinPoolforkJoinPool=newForkJoinPool();ForkJoinTask<整数>forkJoinTaskResult=forkJoinPool.submit(newFibonacciForkJoinTask(4));System.out.println(forkJoinTaskResult.get());}}ForkJoinTask及其子类概述ForkJoinTask是向ForkJoinPool提交任务的基类,是一个抽象类。RecursiveTask和RecursiveAction,顾名思义,就是用来递归切割任务粒度的。不同的是RecursiveTask有返回值,而RecursiveAction没有返回值。RecursiveTask,RecursiveAction1.7推出,CountedCompleter有一个钩子函数onCompletion(CountedCompletercaller),当所有任务完成时会触发该方法。上面用Fork/Join计算斐波那契数列只是为了演示用法,单线程运行速度很快,还有更快的算法。并行流也默认使用ForkJoinPool,建议使用commonPool来使用ForkJoinPool。总而言之,我们对事物的认识是一个循序渐进、清晰的过程。这篇文章算是ForkJoin的入门文章,费了九牛二虎之力才看懂。调用fork方法没有任何作用,最后放弃。ForkJoin并非适用于所有场景。如果我们能够更好地控制任务粒度,那么ForkJoinPool和ThreadPoolExecutor在执行速度上其实没有区别。你也可以只分叉而不加入。参考资料《Java多线程编程实战指南》第2版黄文海的书ThreadPoolExecutorvsForkJoinPool:窃取子任务https://stackoverflow.com/que...ForkJoin实际应用https://juejin.cn/post/698395...Stack-based有哪些虚拟机常用的优化策略?——来自知乎