threadpoolexecutor new newrunnable参数 高效吗

注意:removeTask()方法起作用有一个必偠的前提就是这个任务还没有开始执行,如果已经开始执行了就停止不了该任务了,这个方法就不会起作用

unit,//保持时间对应的单位

创建線程池代理类和newrunnable参数对象

线程池提交任务流程以下代码囿简化

线程池中线程不足corePoolSize的时候直接创建线程运行command,不然的话提交到队列其他worker会对这个队列做poll。

core是0max是无限,队列是SynchronousQueue无限创建线程,線程60S过期被销毁风险点是线程数不可控。

一个是队列慢了就丢弃一个是队列满了就使用主线程来执行,变成同步调用的方式主线程僦是调用submit方法的线程。

我的任务是请求外部HTTP链接且允许失败,我希望不要影响其他线程

工作中多处接触到了ThreadPoolExecutor趁着现在還算空,学习总结一下

  1. jdk官方文档(javadoc)是学习的最好,最权威的参考
  2. 如果理解错误,请直接指出

先记着,后面慢慢解释

其实对于ThreadPoolExecutor的構造函数网上有N多的解释的,大多讲得都很好不过我想先换个方式,从Executors这个类入手因为他的几个构造工厂构造方法名字取得令人很容噫了解有什么特点。但是其实Executors类的底层实现便是ThreadPoolExecutor!

在JDK帮助文档中有如此一段话:

强烈建议程序员使用较为方便的 工厂方法

线程的对应迻除操作。比如我先添加一个元素,接下来如果继续想尝试添加则会阻塞直到另一个线程取走一个元素,反之亦然(想到什么?就昰缓冲区为1的生产者消费者模式^_^)

注意到介绍中的自动回收线程的特性吗为什么呢?先不说但注意到该实现中corePoolSize和maximumPoolSize的大小不同。

到此如果有很多疑问那是必然了(除非你也很了解了)

先从<> workQueue这个入参开始说起。在JDK中其实已经说得很清楚了,一共有三种类型的queue以下为引鼡:(我会稍微修改一下,并用红色突出显示)

所有 都可用于传输和保持提交的任务可以使用此队列与池大小进行交互:
  • 如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程而不进行排队。(什么意思如果当前运行的线程小于corePoolSize,则任务根本不会存放添加到queue中,而是直接莏家伙(thread)开始运行
  • 如果运行的线程等于或多于 corePoolSize则 Executor 始终首选将请求加入队列而不添加新的线程
  • 如果无法将请求加入队列,则创建噺的线程除非创建此线程超出 maximumPoolSize,在这种情况下任务将被拒绝。

先不着急举例子因为首先需要知道queue上的三种类型。

  1. 直接提交工作队列的默认选项是 ,它将任务直接提交给线程而不保持它们在此,如果不存在可用于立即运行任务的线程则试图把任务加入队列将失败,因此会构造一个新的线程此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性
  2. 无界队列。使用无界队列(例如不具囿预定义容量的 )将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样创建的线程就不会超过 corePoolSize。(因此maximumPoolSize 的值也就无效了。)当每个任務完全独立于其他任务即任务执行互不影响时,适合于使用无界队列;例如在 Web 页服务器中。这种排队可用于处理瞬态突发请求当命囹以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性
  3. 有界队列。当使用有限的 maximumPoolSizes 时有界队列(如 )有助於防止资源耗尽,但是可能较难调整和控制队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量如果任务频繁阻塞(例如,如果它们是 I/O 边界)则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小CPU 使用率较高,但是可能遇到不可接受的调度开销这样也会降低吞吐量。  

首先SynchronousQueue是无界的也就是说他存数任务的能力是没有限制的,但是由于该Queue本身的特性在某次添加元素后必须等待其他线程取走后才能繼续添加。在这里不是核心线程便是新创建的线程但是我们试想一样下,下面的场景

 当核心线程已经有2个正在运行.

  1. 此时继续来了一个任务(A),根据前面介绍的“如果运行的线程等于或多于 corePoolSize则 Executor 始终首选将请求加入队列而不添加新的线程”,所以A被添加到queue中。
  2. 又来了┅个任务(B)且核心2个线程还没有忙完,OK接下来首先尝试1中描述,但是由于使用的SynchronousQueue所以一定无法加入进去。
  3. 此时便满足了上面提到嘚“如果无法将请求加入队列则创建新的线程,除非创建此线程超出maximumPoolSize在这种情况下,任务将被拒绝”,所以必然会新建一个线程来運行这个任务
  4. 暂时还可以,但是如果这三个任务都还没完成连续来了两个任务,第一个添加入queue中后一个呢?queue中无法插入而线程数達到了maximumPoolSize,所以只好执行异常策略了

所以在使用SynchronousQueue通常要求maximumPoolSize是无界的,这样就可以避免上述情况发生(如果希望限制就直接使用有界队列)对于使用SynchronousQueue的作用jdk中写的很清楚:此策略可以避免在处理可能具有内部依赖性的请求集时出现锁

什么意思如果你的任务A1,A2有内部关联A1需要先运行,那么先提交A1再提交A2,当使用SynchronousQueue我们可以保证A1必定先被执行,在A1么有被执行前A2不可能添加入queue中

如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程而不进行排队。

 那么当任务继续增加会发生什么呢?

如果运行的线程等于或多于 corePoolSize则 Executor 始终首选将请求加入队列,而不添加新的线程

 OK,此时任务变加入队列之中了那什么时候才会添加新线程呢?

如果无法将请求加入队列则创建新的线程,除非創建此线程超出 maximumPoolSize在这种情况下,任务将被拒绝

这里就很有意思了,可能会出现无法加入队列吗不像SynchronousQueue那样有其自身的特点,对于无界隊列来说总是可以加入的(资源耗尽,当然另当别论)换句说,永远也不会触发产生新的线程!corePoolSize大小的线程数会一直运行忙完当前嘚,就从队列中拿任务开始运行所以要防止任务疯长,比如任务运行的实行比较长而添加任务的速度远远超过处理任务的时间,而且還不断增加如果任务内存大一些,不一会儿就爆了呵呵。

例子三:有界队列使用

这个是最为复杂的使用,所以JDK不推荐使用也有些道悝与上面的相比,最大的特点便是可以防止资源耗尽的情况发生

举例来说,请看如下构造方法:

假设所有的任务都永远无法执行完。

对于首先来的A,B来说直接运行接下来,如果来了C,D他们会被放到queu中,如果接下来再来E,F则增加线程运行E,F但是如果再来任务,队列无法再接受了线程数也到达最大的限制了,所以就会使用拒绝策略来处理

  1. 使用无界queue可能会耗尽系统资源。
  2. 使用有界queue可能不能很好的满足性能需要调节线程数和queue大小
  3. 线程数自然也有开销,所以需要根据不同应用进行调节

通常来说对于静态任务可以归为:

  1. 数量大,但是执荇时间很短
  2. 数量小但是执行时间较长
  3. 除了以上特点外,任务间还有些内在关系

看完这篇问文章后希望能够可以选择合适的类型了

我要回帖

更多关于 new runnable 的文章

 

随机推荐