`
54wangyong
  • 浏览: 13994 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

ThreadPoolExecutor 的一些参数翻译说明

阅读更多

 

     本文针对初学者可能有点不适合,当对多线程有点了解,再看这些属性的说明会理解该线程池工厂的一些特性;

类图:



 

 ======================================================================
有情提醒:
线程池不使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样 的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明: Executors 返回的线程池对象的弊端如下:

       FixedThreadPool 和 SingleThreadPool : 允许的请求队列长度为 Integer.MAX_VALUE ,可能会堆积大量的请求,从而导致 OOM 。

       CachedThreadPool 和 ScheduledThreadPool : 允许的创建线程数量为 Integer.MAX_VALUE ,可能会创建大量的线程,从而导致 OOM 。

 

1. 线程池中主要字段说明

线程池控制状态,ctl,是一个原子封装的integer.它包含了两个概念字段:
workerCount: 表示有效的线程数;
runState:表示是否是运行,停止等等状态;
 

1.1 workerCount

为了将它们集成到一个int中,我们限制workerCount值是(2^29)-1 大约500million(5亿)个线程,
如果在以后这个值的大小有问题,可以给修改成AtomicLong,并且需要调整偏移量,掩码常量;
但是直到真正的需要出现前,使用int将让代码更快更简单;
 
workerCount代表线程已经启动工作的数量,并不代表停止的数量;该值可能与实际存活的线程数量不同;
例如:当TreadFactory创建一个请求线程失败时,
例如,当ThreadFactory无法创建线程时
      询问,并且退出线程仍在执行时
      终止之前记账。 用户可见池大小报告为工作集的当前大小。
 
 

1.2 runState

runState 提供了整个生命周期的控制,主要有以下几种值:
RUNNING: 接受新的任务,并且处理队列中已经有的任务;
SHUTDOWN:不会再接受新的任务,但是还会处理队列中的任务;
STOP:不能够接受新的任务,不再处理队列中的任务,并且interrupt中断现在执行的任务;
TIDYING: 所有的任务都终止了,workerCount是0,线程一直处在TIDYING状态直到调用terminated()方法;
TERMINATED: terminated()方法执行完成了
 
这些值之间的数值顺序变化很重要,可以允许有序的变化。 runState值是单调增加,不会变小,但不必命中每个状态。 值的变化有:
 
RUNNING -> SHUTDOWN
调用shutdown()方法,也有可能是隐匿的执行finalize()方法时(因为该方法内部也是调用shutdown()方法)。
 
RUNNING or SHUTDOWN) -> STOP
调用showdownNow()方法
 
SHUTDOWN -> TIDYING
当线程池和队列都是空时 可以通过队列的isEmpty() 方法来判断线程池是否为空
 
STOP -> TIDYING
线程池是空的
 
TIDYING -> TERMINATED
调用terminated()完成后
 

1.3 workQueue

这个队列是持有任务并且能够拿出来给到工作者线程;当workQueue.isEmpty() 时我们不要求workQueue.poll() 方法立即返回null值回来。所以我们可以通过workQueue.isEmpty()查看队列是否为空。(例如我们想将线路池的状态从SHUTDOWN 转换到TIDYING时,可以通过该种方式来判断)
 
但是会有特殊的队列存在,如果我们使用的是DelayQueeues时,在调用poll()方法时如果超时了允许返回null
 

1.4 mainLock

保持访问工作线程集合和相关的工作线程的锁。虽然我们可以使用某种并发集,但事实证明通常使用锁还是最好的。 原因之一是
      这会序列化interruptIdleWorkers,从而避免了不必要的中断风暴,尤其是在关机期间。否则退出线程会同时中断那些尚未中断的线程。 这也简化了一些最大的PoolSize等的相关统计簿记。我们还要在shutdown和shutdownNow时保持mainLock,以便确保工作线程集合稳定下来时能够检查哪些允许中断和哪些实际中断。
 

1.5 workers

包含了所有在池中的工作线程。只有持有mainLock锁才能够访问该集合;
 

1.6 volatile修饰的属性

以下这些值保证了可见性。因些这些参数不需要mainLock锁来持有访问,当我们访问时可以拿到最新的值;
threadFactory: 新线程的工厂。所有线程都使用此工厂创建(通过调用该工厂的addWorker方法)。所有调用者必须做好使用addWorker添加失败的准备,这可以通过系统或用户提供的限制线程数后使用的策略来处理。即使不是线程本身的error,创建线程失败时可能会导致新任务被拒绝或现有任务被卡在 队列中。
我们运行的越久,在尝试创建线程时,可能会抛出诸如OutOfMemoryError的错误。这个错误是因为,在Thread.start方法执行时需要分配本机堆栈,并且用户需要清理已经很干净的池关闭。那里可能有足够的内存供清理代码使用完成而不会遇到另一个OutOfMemoryError。
 
handler: 当线程饱合或者执行关闭程序时才会调用该handler。
系统中提供的饱合策略有:
默认的饱合策略是AbortPolicy。如果线程池的队列饱合后,会抛出throw new RejectedExecutionException异常;
DiscardPolicy:丢失策略。当饱合时该条任务直接丢弃掉;
DiscardOldestPolicy:丢弃掉老的。会丢弃掉未处理的请求任务中最老的任务。通过获取当前的workQueue的poll()方法丢失掉,然后执行继续执行ThreadPoolExecutor的execute方法执行该任务,如果队列还是满的,还是执行该策略,直接访任务能够添加到队列,或者能被直接执行为止;但是如果是执行者被关闭了,那么该条任务也会被丢弃掉;
CallerRunsPolicy:如果执行者没有shutdown,那么这个任务会被直接调用run方法来执行。如果ThreadPoolExecutor已经shutDown了,那么这个任务也会被丢弃掉;
 
通过以上说明也可以看出,饱合策略是遵循了线程池的runState状态字段的。而上面的shutDown并不是指线程池的runState是在shutdown这个值,而是只要不是running即可,因为调用的是isShutDown()方法
 
 
keepAliveTime: 空闲的线程等待超时的时间(以纳秒单位)。当线程数据超过corePoolSize时,或者设置allowCoreThreadTimeOut为true时,空闲的线程的超时时间会生效;否则线程会一直等待从事新的工作,不会停止掉;
 
allowCoreThreadTimeOut:默认值是false,空闲的核心线程会一直存活动。 如果设置为true时,空闲的核心线程会保持到keepAliveTime这个时长;
 
corePoolSize:核心线程数是保持存活的workes的最小数量(如果没有设置核心线程数超时的话);如果设置了allowCoreThreadTimeOut为true,那么最小的数量就是0
 
maximumPoolSize:最大线程数量。注意实际最大值是受线程池容量的限制的;
 
 
 

1.6 其它一些属性说明

largestPoolSize : 跟踪达到最大池的大小。只有在持有mainLock锁的情况下才能够访问
completedTaskCount: 完成任务的计数器,仅仅在工作线程终止后才会进行更新;该参数也是只有在持有mainLock锁的情况下才能够访问;
 

2. 内部重要类说明

 

2.1 Worker

 
/ **
      * Class Worker主要维护中断控制状态
      *线程运行任务,以及其他次要簿记。
      *此类是机会地扩展AbstractQueuedSynchronizer
      *简化获取和释放每个锁的锁定
      *任务执行。 这样可以防止出现以下情况的中断:
      *旨在唤醒等待任务执行的工作线程
      *而是中断正在运行的任务。 我们实现一个简单的
      *非重入互斥锁,而不是使用
      * ReentrantLock,因为我们不希望工作人员能够执行任务
      *当他们调用诸如池控制方法时,重新获取锁
      * setCorePoolSize。 此外,抑制中断直到
      *线程实际上开始运行任务,我们初始化锁
      *声明为负值,并在启动时将其清除(在
      * runWorker)。
      * /
 

 

3. 方法说明

3.1 execute(Runnable)

 
在将来的某个时间会执行给定的任务。这个任务有可能在新线程中执行,也有可能是在池中已经存在的线程中进行执行;
如果这个任务不能submitted执行,原因就是这个执行器已经shutdown,或者是因为容量已经达到了。那么些时这个任务就是饱合策略来执行了;
 
处理分为三个步骤:
第一步:如果当前运行的线程数据小于核心线程数量时,就会尝试启动一个新的线程来执行这个任务,并且worke的first task就是当前的任务;
 
第二步:如果当前线程池还在行动中,就将要执行的任务放入到队列中;然后二次检查一下当前线程池的状态,如果线程池的状态不是在运行中,就将当前的任务从队列中移出;并且执行饱合策略;如果在运行状态,但是当前没有线程运行了,那么就需要新添加一个线程;
 
第三步:如果当前线程不在运行中,或者不能添加到队列中,那么就尝试创建一个新的线程来执行,因为有可能线程的数量还没有超过最大线程数量;如果还是添加不成功,那么也会执行饱合策略;
 
  • 大小: 15 KB
  • 大小: 23.1 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics