本网站(662p.com)打包出售,且带程序代码数据,662p.com域名,程序内核采用TP框架开发,需要联系扣扣:2360248666 /wx:lianweikj
精品域名一口价出售:1y1m.com(350元) ,6b7b.com(400元) , 5k5j.com(380元) , yayj.com(1800元), jiongzhun.com(1000元) , niuzen.com(2800元) , zennei.com(5000元)
需要联系扣扣:2360248666 /wx:lianweikj
你不知道的线程池构造方法的那些趣事?
吴振华 · 422浏览 · 发布于2019-10-16 +关注

ThreadPoolExecutor

(手机横屏看源码更方便)


注:java源码分析部分如无特殊说明均基于 java8 版本。

简介

ThreadPoolExecutor的构造方法是创建线程池的入口,虽然比较简单,但是信息量很大,由此也能引发一系列的问题,同样地,这也是面试中经常被问到的问题,下面彤哥只是列举了一部分关于ThreadPoolExecutor构造方法的问题,如果你都能回答上来,则可以不用看下面的分析了。

问题

(1)ThreadPoolExecutor有几个构造方法?

(2)ThreadPoolExecutor最长的构造方法有几个参数?

(3)keepAliveTime是做什么用的?

(7)核心线程会不会超时关闭?能不能超时关闭?

(4)ConcurrentLinkedQueue能不能作为任务队列的参数?

(5)默认的线程是怎么创建的?

(6)如何实现自己的线程工厂?

(7)拒绝策略有哪些?

(8)默认的拒绝策略是什么?

构造方法

好了,我们直接上代码。

public
 
ThreadPoolExecutor
(
int
 corePoolSize,

                          
int
 maximumPoolSize,

                          
long
 keepAliveTime,

                          TimeUnit unit,

                          BlockingQueue<Runnable> workQueue)
 
{

    
this
(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,

         Executors.defaultThreadFactory(), defaultHandler);

}


public
 
ThreadPoolExecutor
(
int
 corePoolSize,

                          
int
 maximumPoolSize,

                          
long
 keepAliveTime,

                          TimeUnit unit,

                          BlockingQueue<Runnable> workQueue,

                          ThreadFactory threadFactory)
 
{

    
this
(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,

         threadFactory, defaultHandler);

}


public
 
ThreadPoolExecutor
(
int
 corePoolSize,

                          
int
 maximumPoolSize,

                          
long
 keepAliveTime,

                          TimeUnit unit,

                          BlockingQueue<Runnable> workQueue,

                          RejectedExecutionHandler handler)
 
{

    
this
(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,

         Executors.defaultThreadFactory(), handler);

}


public
 
ThreadPoolExecutor
(
int
 corePoolSize,

                          
int
 maximumPoolSize,

                          
long
 keepAliveTime,

                          TimeUnit unit,

                          BlockingQueue<Runnable> workQueue,

                          ThreadFactory threadFactory,

                          RejectedExecutionHandler handler)
 
{

    
if
 (corePoolSize < 
0
 ||

        maximumPoolSize <= 
0
 ||

        maximumPoolSize < corePoolSize ||

        keepAliveTime < 
0
)

        
throw
 
new
 IllegalArgumentException();

    
if
 (workQueue == 
null
 || threadFactory == 
null
 || handler == 
null
)

        
throw
 
new
 NullPointerException();

    
this
.acc = System.getSecurityManager() == 
null
 ?

            
null
 :

            AccessController.getContext();

    
this
.corePoolSize = corePoolSize;

    
this
.maximumPoolSize = maximumPoolSize;

    
this
.workQueue = workQueue;

    
this
.keepAliveTime = unit.toNanos(keepAliveTime);

    
this
.threadFactory = threadFactory;

    
this
.handler = handler;

}

ThreadPoolExecutor有四个构造方法,其中前三个最终都是调用最后一个,它有7个参数,分别为corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。

corePoolSize

核心线程数。

当正在运行的线程数小于核心线程数时,来一个任务就创建一个核心线程;

当正在运行的线程数大于或等于核心线程数时,任务来了先不创建线程而是丢到任务队列中。

maximumPoolSize

最大线程数。

当任务队列满了时【本篇文章由公众号“彤哥读源码”原创】,来一个任务才创建一个非核心线程,但不能超过最大线程数。

keepAliveTime + unit

线程保持空闲时间及单位。

默认情况下,此两参数仅当正在运行的线程数大于核心线程数时才有效,即只针对非核心线程。

但是,如果allowCoreThreadTimeOut被设置成了true,针对核心线程也有效。

即当任务队列为空时,线程保持多久才会销毁,内部主要是通过阻塞队列带超时的poll(timeout, unit)方法实现的。

workQueue

任务队列。

当正在运行的线程数大于或等于核心线程数时,任务来了是先进入任务队列中的。

这个队列必须是阻塞队列,所以像ConcurrentLinkedQueue就不能作为参数,因为它虽然是并发安全的队列,但是它不是阻塞队列。

// ConcurrentLinkedQueue并没有实现BlockingQueue接口public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>        implements Queue<E>, java.io.Serializable {    // ...【本篇文章由公众号“彤哥读源码”原创】}

threadFactory

线程工厂。

默认使用的是Executors工具类中的DefaultThreadFactory类,这个类有个缺点,创建的线程的名称是自动生成的,无法自定义以区分不同的线程池,且它们都是非守护线程。

static
 
class
 
DefaultThreadFactory
 
implements
 
ThreadFactory
 
{

        
private
 
static
 
final
 AtomicInteger poolNumber = 
new
 AtomicInteger(
1
);

        
private
 
final
 ThreadGroup group;

        
private
 
final
 AtomicInteger threadNumber = 
new
 AtomicInteger(
1
);

        
private
 
final
 String namePrefix;


        DefaultThreadFactory() {

            SecurityManager s = System.getSecurityManager();

            group = (s != 
null
) ? s.getThreadGroup() :

                                  Thread.currentThread().getThreadGroup();

            namePrefix = 
"pool-"
 +

                          poolNumber.getAndIncrement() +

                         
"-thread-"
;

        }


        
public
 Thread 
newThread
(Runnable r)
 
{

            Thread t = 
new
 Thread(group, r,

                                  namePrefix + threadNumber.getAndIncrement(),

                                  
0
);

            
if
 (t.isDaemon())

                t.setDaemon(
false
);

            
if
 (t.getPriority() != Thread.NORM_PRIORITY)

                t.setPriority(Thread.NORM_PRIORITY);

            
return
 t;

        }

    }

那怎么自定义一个线程工厂呢?

其实也很简单,自己实现一个ThreadFactory,然后把名称和是否是守护进程当作构造方法的参数传进来就可以了。

有兴趣的同学可以参考netty中的默认线程工厂或者google中的线程工厂。

io.netty.util.concurrent.DefaultThreadFactorycom.google.common.util.concurrent.ThreadFactoryBuilder

handler

拒绝策略。

拒绝策略表示当任务队列满了且线程数也达到最大了,这时候再新加任务,线程池已经无法承受了,这些新来的任务应该按什么逻辑来处理。

常用的拒绝策略有丢弃当前任务、丢弃最老的任务、抛出异常、调用者自己处理等待。

默认的拒绝策略是抛出异常,即线程池无法承载了,调用者再往里面添加任务会抛出异常。

默认的拒绝策略虽然比较简单粗暴,但是相对于丢弃任务策略明显要好很多,最起码调用者自己可以捕获这个异常再进行二次处理。

彩蛋

OK,ThreadPoolExecutor的构造方法这块我们今天进行了深入解析,关于这块,您还有什么问题呢?欢迎留言评论、私聊勾搭。

相关推荐

PHP实现部分字符隐藏

沙雕mars · 1312浏览 · 2019-04-28 09:47:56
Java中ArrayList和LinkedList区别

kenrry1992 · 896浏览 · 2019-05-08 21:14:54
Tomcat 下载及安装配置

manongba · 957浏览 · 2019-05-13 21:03:56
JAVA变量介绍

manongba · 953浏览 · 2019-05-13 21:05:52
什么是SpringBoot

iamitnan · 1076浏览 · 2019-05-14 22:20:36
加载中

0评论

评论
坐标是江苏.南京,行业是互联网,技术是PHP和java,还有熟悉前后端等。
分类专栏
小鸟云服务器
扫码进入手机网页