手写线程池

Posted dengw125792

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手写线程池相关的知识,希望对你有一定的参考价值。

Executors.newSingleThreadExecutor():

        只有一个线程的线程池,因此所有提交的任务是顺序执行

Executors.newCachedThreadPool():

        线程池里有很多线程需要同时执行,老的可用线程将被新的任务触发重新执行,

        如果线程超过60秒内没执行,那么将被终止并从池中删除

Executors.newFixedThreadPool():

         拥有固定线程数的线程池,如果没有任务执行,那么线程会一直等待

Executors.newScheduledThreadPool():

         用来调度即将执行的任务的线程池

Executors.newWorkStealingPool():

          newWorkStealingPool适合使用在很耗时的操作,

         但是newWorkStealingPool不是ThreadPoolExecutor的扩展,

         它是新的线程池类ForkJoinPool的扩展,但是都是在统一的一个Executors类中实现,

          由于能够合理的使用CPU进行对任务操作(并行操作),所以适合使用在很耗时的任务中

 

生产环境不使用以上线程池,主要原因是,以上线程池底层使用的LinkedBlockingQueue链表阻塞

队列,这样最大值为21亿,范围过大,会造成OOM异常。

需要使用ThreadPoolExecutor传递7个参数手工实现线程池。

 

技术图片

 

 

 

ThreadPoolExecutor

ThreadPoolExecutor作为java.util.concurrent包对外提供基础实现,以内部线程池的形式对外提供管理任务执行,

线程调度,线程池管理等等服务。

corePoolSize 核心线程池大小
maximumPoolSize 最大线程池大小
keepAliveTime 线程池中超过 corePoolSize 数目的空闲线程最大存活时间;可以allowCoreThreadTimeOut(true) 使得核心线程有效时间
TimeUnit keepAliveTime 时间单位
workQueue 阻塞任务队列
threadFactory 新建线程工厂
RejectedExecutionHandler 当提交任务数超过 maxmumPoolSize+workQueue 之和时,任务会交给RejectedExecutionHandler 来处理

 

 

Java中的BlockingQueue主要有两种实现,分别是ArrayBlockingQueue 和 LinkedBlockingQueue。

ArrayBlockingQueue是一个用数组实现的有界阻塞队列,必须设置容量。

LinkedBlockingQueue是一个用链表实现的有界阻塞队列,容量可以选择进行设置,

不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE。

这里的问题就出在:不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE。

也就是说,如果我们不设置LinkedBlockingQueue的容量的话,其默认容量将会是Integer.MAX_VALUE。

 

=================测试===============================================

package t1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestExecutors {

public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(2, // corePoolSize 当日值班线程
5, // maximumPoolSize  最大可提供线程
1, // keepAliveTime  除开值班线程等待时间,等待这么多时间后,除开值班线程的其他线程销毁
TimeUnit.SECONDS, // unit  等待时间的单位 ,等待这么多时间后,除开值班线程的其他线程销毁
new LinkedBlockingDeque<>(3), // workQueue  阻塞队列的类型以及大小
Executors.defaultThreadFactory(), // threadFactory 配置 默认即可
new ThreadPoolExecutor.AbortPolicy());// handler 线程多于maximumPoolSize+LinkedBlockingDeque后会报错

// new ThreadPoolExecutor.CallerRunsPolicy());//handler 多的交给调用他的线程处理
// new ThreadPoolExecutor.DiscardOldestPolicy());//handler 丢掉等待最久的线程
// new ThreadPoolExecutor.DiscardPolicy());//handler 丢掉处理不过来的线程

for (int i = 0; i < 9; i++) {
executorService.execute(() -> {
System.out.println(Thread.currentThread().getName());

});

}
}

}

 

输出结果根据拒绝策略而异。

 

 *********************************************************

corePoolSize 参数配置规则

CPU 密集型:比如while循环一开,CPU就很高上去了这种

       CPU 密集的意思是该任务需要大量的运算,而没有阻塞,CPU 一直全速运行。

       CPU 密集型任务尽可能的少的线程数量,一般为 CPU 核数 + 1 个线程的线程池。

IO 密集型:比如磁盘IO特别频繁这种

       由于 IO 密集型任务线程并不是一直在执行任务,可以多分配一点线程数,如 CPU * 2 。

        也可以使用公式:CPU 核数 / (1 - 阻塞系数);其中阻塞系数在 0.8 ~ 0.9 之间。

 

以上是关于手写线程池的主要内容,如果未能解决你的问题,请参考以下文章

手写一个线程池,带你学习ThreadPoolExecutor线程池实现原理

手写一个线程池

线程池理念分析及其手写

手写线程池

手写简单的线程池

线程池的手写和拒绝策略