[Java并发编程(一)] 线程池 FixedThreadPool vs CachedThreadPool ...

摘要

介绍 Java 并发包里的几个主要 ExecutorService 。

正文

CachedThreadPool

CachedThreadPool 是通过 java.util.concurrent.Executors 创建的 ThreadPoolExecutor 实例。这个实例会根据需要,在线程可用时,重用之前构造好的池中线程。这个线程池在执行 大量短生命周期的异步任务时(many short-lived asynchronous task),可以显著提高程序性能。调用 execute 时,可以重用之前已构造的可用线程,如果不存在可用线程,那么会重新创建一个新的线程并将其加入到线程池中。如果线程超过 60 秒还未被使用,就会被中止并从缓存中移除。因此,线程池在长时间空闲后不会消耗任何资源。

注意队列实例是:new SynchronousQueue()


/**
* Creates a thread pool that creates new threads as needed, but
* will reuse previously constructed threads when they are
* available. These pools will typically improve the performance
* of programs that execute many short-lived asynchronous tasks.
* Calls to <tt>execute</tt> will reuse previously constructed
* threads if available. If no existing thread is available, a new
* thread will be created and added to the pool. Threads that have
* not been used for sixty seconds are terminated and removed from
* the cache. Thus, a pool that remains idle for long enough will
* not consume any resources. Note that pools with similar
* properties but different details (for example, timeout parameters)
* may be created using {@link ThreadPoolExecutor} constructors.
*
* @return the newly created thread pool
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}

FixedThreadPool

FixedThreadPool 是通过 java.util.concurrent.Executors 创建的 ThreadPoolExecutor 实例。这个实例会复用 固定数量的线程 处理一个 共享的无边界队列 。任何时间点,最多有 nThreads 个线程会处于活动状态执行任务。如果当所有线程都是活动时,有多的任务被提交过来,那么它会一致在队列中等待直到有线程可用。如果任何线程在执行过程中因为错误而中止,新的线程会替代它的位置来执行后续的任务。所有线程都会一致存于线程池中,直到显式的执行 ExecutorService.shutdown() 关闭。

注意队列实例是:new LinkedBlockingQueue()


/**
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue. At any point, at most
* <tt>nThreads</tt> threads will be active processing tasks.
* If additional tasks are submitted when all threads are active,
* they will wait in the queue until a thread is available.
* If any thread terminates due to a failure during execution
* prior to shutdown, a new one will take its place if needed to
* execute subsequent tasks. The threads in the pool will exist
* until it is explicitly {@link ExecutorService#shutdown shutdown}.
*
* @param nThreads the number of threads in the pool
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}

SingleThreadPool

SingleThreadPool 是通过 java.util.concurrent.Executors 创建的 ThreadPoolExecutor 实例。这个实例只会使用单个工作线程来执行一个无边界的队列。(注意,如果单个线程在执行过程中因为某些错误中止,新的线程会替代它执行后续线程)。它可以保证认为是按顺序执行的,任何时候都不会有多于一个的任务处于活动状态。和 newFixedThreadPool(1) 的区别在于,如果线程遇到错误中止,它是无法使用替代线程的。


/**
* Creates an Executor that uses a single worker thread operating
* off an unbounded queue. (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* <tt>newFixedThreadPool(1)</tt> the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
*
* @return the newly created single-threaded Executor
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}

程序演示

  • LiftOff


    public class LiftOff implements Runnable {
    protected int countDown = 10; // Default
    private static int taskCount = 0;
    private final int id = taskCount++; public LiftOff() {} public LiftOff(int countDown) {
    this.countDown = countDown;
    } public String status() {
    return "Thread ID: [" + String.format("%3d", Thread.currentThread().getId()) + "] #" + id + "(" + (countDown > 0 ? countDown : "LiftOff!") + ") ";
    } @Override
    public void run() {
    while (countDown-- > 0) {
    System.out.println(status());
    Thread.yield();
    }
    } }
  • CachedThreadPoolCase


    public class CachedThreadPoolCase {
    public static void main(String[] args) throws InterruptedException {
    ExecutorService exec = Executors.newCachedThreadPool();
    for(int i = 0; i < 5; i++) {
    exec.execute(new LiftOff());
    Thread.sleep(10);
    }
    exec.shutdown();
    }
    }

    当 sleep 间隔为 5 milliseconds 时,共创建了 3 个线程,并交替执行。

    当 sleep 间隔为 10 milliseconds 时,共创建了 2 个线程,交替执行。

  • FixedThreadPoolCase


    public class FixedThreadPoolCase { public static void main(String[] args) throws InterruptedException {
    ExecutorService exec = Executors.newFixedThreadPool(3);
    for (int i = 0; i < 5; i++) {
    exec.execute(new LiftOff());
    Thread.sleep(10);
    }
    exec.shutdown();
    }
    }

    无论 sleep 间隔时间是多少,总共都创建 3 个线程,并交替执行。

  • SingleThreadCase


    public class SingleThreadPoolCase { public static void main(String[] args) throws InterruptedException {
    ExecutorService exec = Executors.newSingleThreadExecutor();
    for (int i = 0; i < 10; i++) {
    exec.execute(new LiftOff());
    Thread.sleep(5);
    }
    exec.shutdown();
    }
    }

    无论 sleep 间隔时间是多少,总共都只创建 1 个线程。

FixedThreadPool 与 CachedThreadPool 特性对比

特性 FixedThreadPool CachedThreadPool
重用 FixedThreadPool 与 CacheThreadPool差不多,也是能 reuse 就用,但不能随时建新的线程 缓存型池子,先查看池中有没有以前建立的线程,如果有,就 reuse ;如果没有,就建一个新的线程加入池中
池大小 可指定 nThreads,固定数量 可增长,最大值 Integer.MAX_VALUE
队列大小 无限制 无限制
超时 无 IDLE 默认 60 秒 IDLE
使用场景 所以 FixedThreadPool 多数针对一些很稳定很固定的正规并发线程,多用于服务器 大量短生命周期的异步任务
结束 不会自动销毁 注意,放入 CachedThreadPool 的线程不必担心其结束,超过 TIMEOUT 不活动,其会自动被终止。

最佳实践

FixedThreadPool 和 CachedThreadPool 两者对高负载的应用都不是特别友好。

CachedThreadPool 要比 FixedThreadPool 危险很多。

如果应用要求高负载、低延迟,最好不要选择以上两种线程池:

  1. 任务队列的无边界:会导致内存溢出以及高延迟
  2. 长时间运行会导致 CachedThreadPool 在线程创建上失控

因为两者都不是特别友好,所以推荐使用 ThreadPoolExecutor ,它提供了很多参数可以进行细粒度的控制。

  1. 将任务队列设置成有边界的队列

  2. 使用合适的 RejectionHandler - 自定义的 RejectionHandler 或 JDK 提供的默认 handler 。

  3. 如果在任务完成前后需要执行某些操作,可以重载

     beforeExecute(Thread, Runnable)
    afterExecute(Runnable, Throwable)
  4. 重载 ThreadFactory ,如果有线程定制化的需求

  5. 在运行时动态控制线程池的大小(Dynamic Thread Pool

参考

iteye: Java 并发包中的几种 ExecutorService

stackoverflow: FixedThreadPool vs CachedThreadPool: the lesser of two evils

blogjava: 深入浅出多线程(4)对CachedThreadPool OutOfMemoryError问题的一些想法

结束

[Java并发编程(一)] 线程池 FixedThreadPool vs CachedThreadPool ...的更多相关文章

  1. Java并发编程:线程池的使用

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  2. Java并发编程:线程池的使用(转)

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  3. (转)Java并发编程:线程池的使用

    背景:线程池在面试时候经常遇到,反复出现的问题就是理解不深入,不能做到游刃有余.所以这篇博客是要深入总结线程池的使用. ThreadPoolExecutor的继承关系 线程池的原理 1.线程池状态(4 ...

  4. Java并发编程:线程池的使用(转载)

    转载自:https://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...

  5. Java并发编程:线程池的使用(转载)

    文章出处:http://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...

  6. [转]Java并发编程:线程池的使用

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  7. 【转】Java并发编程:线程池的使用

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  8. 13、Java并发编程:线程池的使用

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  9. Java并发编程之线程池的使用

    1. 为什么要使用多线程? 随着科技的进步,现在的电脑及服务器的处理器数量都比较多,以后可能会越来越多,比如我的工作电脑的处理器有8个,怎么查看呢? 计算机右键--属性--设备管理器,打开属性窗口,然 ...

  10. Java并发编程之线程池及示例

    1.Executor 线程池顶级接口.定义方法,void execute(Runnable).方法是用于处理任务的一个服务方法.调用者提供Runnable 接口的实现,线程池通过线程执行这个 Runn ...

随机推荐

  1. Iview-datePicker获取选中的日期,如果没有选,提示错误,选了,错误隐藏

    最近遇到大坑,,最后解决了,直接上代码 npm intall iview main.js中引入 import iView from 'iview'; import 'iview/dist/styles ...

  2. python数据类型及基本运算符

    1.数据类型 (1)什么是数据类型? 变量值是我们存储的数据,所以数据类型就是变量值的不同种类 (2)为什么要分类型? 变量值是为了保存现实世界中的状态,针对于不同的状态应该用不同的类型去表示 (3) ...

  3. BZOJ2808 : 那些年我们画格子

    若$\min(n,m)=1$,那么设$f[i][j][k]$表示考虑前$i$个格子,改变了$j$次颜色,$i$的颜色为$k$的方案数,直接转移即可. 否则$\min(n,m)\geq 2$,那么有解当 ...

  4. Alpha冲刺(1/10)——2019.4.24

    所属课程 软件工程1916|W(福州大学) 作业要求 Alpha冲刺(1/10)--2019.4.24 团队名称 待就业六人组 1.团队信息 团队名称:待就业六人组 团队描述:同舟共济扬帆起,乘风破浪 ...

  5. oracle增删改字段

    添加字段的语法:alter table tablename add (column datatype [default value][null/not null],….); 修改字段的语法:alter ...

  6. 关于kafka重新消费数据问题

    我们在使用consumer消费数据时,有些情况下我们需要对已经消费过的数据进行重新消费,这里介绍kafka中两种重新消费数据的方法. 1. 修改offset 我们在使用consumer消费的时候,每个 ...

  7. Office Web Apps 2013 修改Excel在线查看文件大小限制

    前言 最近搭建了一个OWA 2013环境,帮客户实现在线查看Excel文档,不过,使用过程中出现了错误,文件大小超过10MB就无法预览了,查了好久,发现需要使用PowerShell命令进行修改. 1. ...

  8. VBV Rate Control

    Part 1 <06/05/07 12:08pm> Manao | he is negating a float by printing it, adding a "-" ...

  9. Python String Formatting Best Practices

    https://imliyan.com/blogs/article/Python3.6%E6%96%B0%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%A0%BC%E5 ...

  10. html5学习笔记3——高级特性

    一:Web存储 数据以 键/值 对存在, web网页的数据只允许该网页访问使用. web存储有两种: localStorage - 没有时间限制的数据存储,存于浏览器缓存 sessionStorage ...