ExecutorService详解

JDK1.5以前,创建线程是通过继承Thread,但是继承Thread存在如下弊端:

1.每次new Thread的时候都需要新建一个线程,性能差。

2.线程缺乏统一管理,可能无限制的新建线程,相互竞争,有可能占用过多系统资源导致死机或者OOM。

3.Thread类缺少更多功能,比如更多的执行、定期执行、线程中断。

JDK1.5之后,系统自带的Executor框架,为开发者们自定义线程池带来了极大的方便。线程池的好处如下:

1.重用存在的线程,减少对象创建、消亡的开销、性能佳。

2.可以有效的控制最大并发线程数,提高系统资源利用率,同时可以避免过多资源竞争,避免阻塞。

3.提供定时执行、定期执行、单线程、并发数控制等功能。

Executors

Executors是一个线程池工厂类,里面有许多静态方法,供开发者调用。

/* 该方法返回一个固定线程数量的线程池,该线程池池中的线程数量始终不变。
 * 当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。
 * 若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务 
 * 默认等待队列长度为Integer.MAX_VALUE
 */
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(1);

/* 该方法返回一个只有一个线程的线程池。
 * 若多余一个任务被提交到线程池,任务会被保存在一个任务队列中,等待线程空闲,按先入先出顺序执行队列中的任务
 * 默认等待队列长度为Integer.MAX_VALUE
 */
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

/* 
 * 该方法返回一个可根据实际情况调整线程数量的线程池。
 * 线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。
 * 若所有线程均在工作,又有新任务的提交,则会创建新的线程处理任务。
 * 所有线程在当前任务执行完毕后,将返回线程池进行复用
 */
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();

/* 该方法返回一个ScheduledExecutorService对象,线程池大小为1。
 * ScheduledExecutorService接口在ExecutorService接口之上扩展了在给定时间内执行某任务的功能,
 * 如在某个固定的延时之后执行,或者周期性执行某个任务
 */
ExecutorService newSingleThreadScheduledExecutor = Executors.newSingleThreadScheduledExecutor();

/*
 * 该方法也返回一个ScheduledExecutorService对象,但该线程池可以指定线程数量
 */
ExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(1);

以上的工具类的具体实现都是基于ThreadPoolExecutor类,处理策略都是AbortPolicy(直接抛出异常,阻止系统正常工作)

Executors 返回的线程池对象的弊端如下:

1) FixedThreadPool 、SingleThreadPool:

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

2) CachedThreadPool 、 ScheduledThreadPool、SingleThreadScheduledExecutor:

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

通常情况下,线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

ThreadPoolExecutor参数描述

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler);

(1)corePoolSize:当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。
(2)maximumPoolSize:线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。
(3)keepAliveTime:当线程池线程数量超过corePoolSize时,多余的空余线程存活时间,即超过corePoolSize的空闲线程,在多长时间内被销毁。如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。
(4)unit:keepAliveTime的单位。
(5)workQueue:任务队列,被提交但尚未被执行任务的任务。有以下几种队列模式:

  1. ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
  2. LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
  3. SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
  4. PriorityBlockingQueue:一个具有优先级得无限阻塞队列。

(6)threadFactory:线程工厂,用于创建线程,一般用默认的即可。也可以通过线程工厂给每个创建出来的线程设置更有意义的名字,Debug和定位问题时非常有帮助。
(7)handler:拒绝策略,当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。系统内置的策略模式如下:

  1. AbortPolicy:直接抛出异常,阻止系统正常工作。
  2. CallerRunsPolicy:只要线程未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。
  3. DiscardOldestPolicy:该策略将丢弃最老的一个请求,也就是即将被执行的一个人任务,并尝试再次提交当前任务。
  4. DiscardPolicy:该策略默默的丢弃无法处理对
  5. 当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。

ThreadPoolExecutor执行流程

1)如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。

2)如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。

3)如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。

4)如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。

ThreadPoolExecutor 例子

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 20, 1,TimeUnit.SECONDS, 
new ArrayBlockingQueue<Runnable>(15),
new ThreadPoolExecutor.DiscardOldestPolicy());

上面的语句:

10是表示允许10个线程,20是最多可以有20个线程在进行或等待,1 线程池维护线程所允许的空闲时间为1秒,TimeUnt.SECONDS 线程池维护线程所允许的空闲时间的单位以秒为单位,下一个参数是指线程池所使用的缓冲队列为有界队列,最后一次参数是线程池对拒绝任务的处理策略抛弃旧的任务.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龍醫師

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值