23体验网带你看看Java线程池的实现原理深入源代码分析

程序的运行本质上是使用系统资源(CPU、内存、磁盘、网络等)。如何有效地利用这些资源是我们编程优化发展的方向。今天的线程池是优化CPU利用率的一种方法。有很多关于如何在Internet上使用线程池的文章。我想说什么?希望通过学习线程池的原理,了解池技术的基本设计思想。其他类似的问题也可以解决。前面提到一个名词,那就是池化技术,那么到底什么是池化技术呢?池化技术也就是说,就是提前保存大量的资源,以备不时之需。在机器资源有限的情况下,使用池化技术可以大大的提高资源的利用率,提升性能等。

在编程领域,比较典型的池化技术有:线程池、连接池、内存池、对象池等。本文主要来介绍一下其中比较简单的线程池的实现原理,希望读者们可以举一反三,通过对线程池的理解,学习并掌握所有编程中池化技术的底层原理。在Java的并发编程中,线程是十分重要的,在Java中,创建一个线程比较容易。

23体验网带你看看Java线程池的实现原理深入源代码分析_第1张图片

我们通过创建一个线程对象,并且实现Runnable接口就可以实现一个简单的线程。可以利用上多核CPU。当一个任务结束,当前线程就接收。但很多时候,我们不止会执行一个任务。如果每次都是如此的创建线程->执行任务->销毁线程,会造成很大的性能开销。那能否一个线程创建后,执行完一个任务后,又去执行另一个任务,而不是销毁。这就是线程池。这也就是池化技术的思想,通过预先创建好多个线程,放在池中,这样可以在需要使用线程的时候直接获取,避免多次重复创建、销毁带来的开销。

以下代码,是在Java中创建线程池:

23体验网带你看看Java线程池的实现原理深入源代码分析_第2张图片

Jdk提供给外部的接口也很简单。直接调用ThreadPoolExecutor构造一个就可以了,也可以通过Executors静态工厂构建,但一般不建议。可以看到,开发者想要在代码中使用线程池还是比较简单的,这得益于Java给我们封装好的一系列API。但是,这些API的背后是什么呢,让我们来揭开这个迷雾,看清线程池的本质。通常,一般构造函数会反映出这个工具或这个对象的数据存储结构。

acc:获取调用上下文

corePoolSize:核心线程数量,可以类比正式员工数量,常驻线程数量。

maximumPoolSize:最大的线程数量,公司最多雇佣员工数量。常驻+临时线程数量。

workQueue:多余任务等待队列,再多的人都处理不过来了,需要等着,在这个地方等。

keepAliveTime:非核心线程空闲时间,就是外包人员等了多久,如果还没有活干,解雇了。

threadFactory:创建线程的工厂,在这个地方可以统一处理创建的线程的属性。每个公司对员工的要求不一样,恩,在这里设置员工的属性。

handler:线程池拒绝策略,什么意思呢?就是当任务实在是太多,人也不够,需求池也排满了,还有任务咋办?默认是不处理,抛出异常告诉任务提交者,我这忙不过来了。

接着,我们看一下线程池中比较重要的execute方法,该方法用于向线程池中添加一个任务。

23体验网带你看看Java线程池的实现原理深入源代码分析_第3张图片

workerCountOf方法根据ctl的低29位,得到线程池的当前线程数,如果线程数小于corePoolSize,则执行addWorker方法创建新的线程执行任务;判断线程池是否在运行,如果在,任务队列是否允许插入,插入成功再次验证线程池是否运行,如果不在运行,移除插入的任务,然后抛出拒绝策略。如果在运行,没有线程了,就启用一个线程;如果添加非核心线程失败,就直接拒绝了。

我们看看如何添加一个工作线程的?从方法execute的实现可以看出:addWorker主要负责创建新的线程并执行任务,代码如下(这里代码有点长,没关系,也是分块的,总共有5个关键的代码块)

判断线程池的状态,如果线程池的状态值大于或等SHUTDOWN,则不处理提交的任务,直接返回;通过参数core判断当前需要创建的线程是否为核心线程,如果core为true,且当前线程数小于corePoolSize,则跳出循环,开始创建新的线程。有人或许会疑问retry是什么?这个是java中的goto语法。只能运用在break和continue后面。获取线程池主锁。线程池的工作线程通过Woker类实现,通过ReentrantLock锁保证线程安全;加线程到workers中(线程池中);启动新建的线程。一个hashSet。所以,线程池底层的存储结构其实就是一个HashSet。

所谓线程池本质是一个hashSet。多余的任务会放在阻塞队列中。只有当阻塞队列满了后,才会触发非核心线程的创建。所以非核心线程只是临时过来打杂的。直到空闲了,然后自己关闭了。线程池提供了两个钩子(beforeExecute,afterExecute)给我们,我们继承线程池,在执行任务前后做一些事情。线程池原理关键技术:锁(lock,cas)、阻塞队列、hashSet(资源池)

最后,我希望这将有助于您理解线程池。最后,让我们思考一下,为什么线程池的底层数据接口使用hashset来实现?合理利用自己的时间每分每秒地学习提高自己,不要用“没有时间”来掩饰自己的精神懒惰!当你年轻的时候,努力工作,给自己一个关于未来的描述!

你可能感兴趣的:(JAVA服务器端技术)