可设置的主要参数


  • corePoolSize

核心线程数,核心线程会一直存活,即使没有任务需要处理。当线程数小于核心线程数时,即使现有的线程空闲,线程池也会优先创建新线程来处理任务,而不是直接交给现有的线程处理。

核心线程在allowCoreThreadTimeout被设置为true时会超时退出,默认情况下不会退出。

  • maxPoolSize

当线程数大于或等于核心线程,且任务队列已满时,线程池会创建新的线程,直到线程数量达到maxPoolSize。如果线程数已等于maxPoolSize,且任务队列已满,则已超出线程池的处理能力,线程池默认会拒绝处理任务而抛出异常。可以自定义处理多余任务的RejectedExecutionHandler

该参数在某些情况下是无效的。

  • keepAliveTime

当已经执行过的线程空闲后,空闲时间小于keepAliveTime,又来了新的任务时,会直接启动该线程。

当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。

如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0。

  • allowCoreThreadTimeout

是否允许核心线程空闲退出,默认值为false。

  • queueCapacity

任务队列容量,由它设定一个有界的队列。从maxPoolSize的描述上可以看出,任务队列的容量会影响到线程的变化,因此任务队列的长度也需要恰当的设置。

  • RejectedExecutionHandler

    public interface RejectedExecutionHandler {
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
    }

    任务拒绝处理策略。在源码中定义了如下几种策略:

    • AbortPolicy (默认策略)
    public static class AbortPolicy implements RejectedExecutionHandler {
    public AbortPolicy() { }
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    throw new RejectedExecutionException("Task " + r.toString() +
    " rejected from " +
    e.toString());
    }
    }

    该策略 直接抛出了异常。异常中止

    • CallerRunsPolicy
    public static class CallerRunsPolicy implements RejectedExecutionHandler {
    public CallerRunsPolicy() { }
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
    r.run();
    }
    }
    }

    在线程池未关闭时,直接在当前线程执行任务。所谓当前线程,即提交任务时的线程

    • DiscardPolicy
    public static class DiscardPolicy implements RejectedExecutionHandler {
    public DiscardPolicy() { }
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
    }

    不做任何处理,即跳过任务r

    • DiscardOldestPolicy
    public static class DiscardOldestPolicy implements RejectedExecutionHandler {
    public DiscardOldestPolicy() { }
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
    e.getQueue().poll();
    e.execute(r);
    }
    }
    }

    在线程池未关闭时,移除处于队头的任务,再执行任务r。相当于把之前队头的任务跳过了,把r又加入了队列

参数对线程池任务调度的影响


当任务数大于等于corePoolSize,这时,除核心线程执行了一定数量的任务后,

余下的需要执行的任务数为int count = tasksCount - corePoolSize,设队列最大容量为capacity:

  • capacity >= count,则只会创建corePoolSize个线程来执行任务。多的任务放在队列中。

    这时maxPoolSize,就无效了

  • capacity < count,这时有int remain = tasksCount - capacity

    • remain <= maxPoolSize,创建remain个线程来执行任务。多的任务放在队列中
    • remain > maxPoolSize,创建maxPoolSize个线程来执行任务;这时将有remain-maxPoolSize个任务无法加入队列,被RejectedExecutionHandler处理。

Executors生成ThreadPoolExecutor


(most from http://dongxuan.iteye.com/blog/901689)

在构建时,corePoolSize、maximumPoolSizes 和BlockingQueue的选择,直接影响线程调度的策略

Executors 有三个方法可以生成 ThreadPoolExecutor

生成一个固定线程数的 ThreadPoolExecutor

public static ExecutorService newFixedThreadPool(int nThreads) {

​ return new ThreadPoolExecutor(nThreads, nThreads,

​ 0L, TimeUnit.MILLISECONDS,

​ new LinkedBlockingQueue());

}

LinkedBlockingQueue实现是线程安全的,实现了先进先出等特性,是作为生产者消费者的首选,LinkedBlockingQueue 可以指定容量,也可以不指定,不指定的话,默认最大是Integer.MAX_VALUE,其中主要用到put和take方法,put方法在队列满的时候会阻塞直到有队列成员被消费,take方法在队列空的时候会阻塞,直到有队列成员被放进来。在ThreadPoolExecutor中主要使用的是BlockingQueue的offer()和 poll()、take()

LinkedBlockingQueue 无界队列

生成一个只含一个线程的ThreadPoolExecutor

public static ExecutorService newSingleThreadExecutor() {

​ return new FinalizableDelegatedExecutorService

​ (new ThreadPoolExecutor(1, 1,

​ 0L, TimeUnit.MILLISECONDS,

​ new LinkedBlockingQueue()));

}

LinkedBlockingQueue 无界队列

生成一个无界的并直接提交的ThreadPoolExecutor

public static ExecutorService newCachedThreadPool() {

​ return new ThreadPoolExecutor(0, Integer.MAX_VALUE,

​ 60L, TimeUnit.SECONDS,

​ new SynchronousQueue());

}

这个线程池根据需要(新任务到来时)创建新的线程,如果有空闲线程则会重复使用,线程空闲了60秒后会被回收

SynchronousQueue 的特点是,每个插入操作必须等待另一个线程的对应移除操作。比如,要添加一个元素,接下来如果继续想尝试添加则会阻塞,直到另一个线程取走一个元素,反之亦然

SynchronousQueue 不会保留任务进队列,会直接提交到工作线程。

如果当前没有可工作线程,那么在 corePoolSize

关于工作队列(workQueue):

所有 BlockingQueue 都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互:

  • 如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。(什么意思?如果当前运行的线程小于corePoolSize,则任务根本不会存放,添加到queue中,而是直接开始运行thread。
  • 如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。
  • 如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。

排队有三种通用策略:

  1. 直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

  2. 无界队列,即队列容量超出余下任务量。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

  3. 有界队列,即队列容量小于余下任务量。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue或具有预定义容量的 LinkedBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。

示例


package com.stone.threadpool;

import java.util.concurrent.*;

/**
* Created by stone on 10/05/2017.
*/

public class Test {
public static void main(String[] args) {
ExecutorService pool = newThreadPool(2);
for (int i = 0; i < 11; i++) {
final int index = i;
pool.execute(()-> {
System.out.println(Thread.currentThread().getName() + " indexA=" + index);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// System.out.println(Thread.currentThread().getName() + " indexB=" + index);
});
}

}

public static ExecutorService newThreadPool(int nThreads) {

// return newFixedThreadPool(nThreads);

// return newSingleThreadPool();

// return newCachedThreadPool();

return newLimitThreadPoll();

}

//生成一个固定线程数的 ThreadPoolExecutor
public static ExecutorService newFixedThreadPool(int nThreads) {

// return new ThreadPoolExecutor(nThreads, nThreads,
return new ThreadPoolExecutor(nThreads, nThreads * 3, //这时 maximumPoolSize没什么用

0, TimeUnit.MILLISECONDS,

new LinkedBlockingQueue<>()); //默认队列容量为Integer.MAX_VALUE

}


//生成一个只含一个线程的ThreadPoolExecutor
public static ExecutorService newSingleThreadPool() {
return new ThreadPoolExecutor(1, 1, //这里增大maximumPoolSize 也是没用的

0L, TimeUnit.MILLISECONDS,

new LinkedBlockingQueue<Runnable>());//默认队列容量为Integer.MAX_VALUE

}

//生成一个无界的并使用缓存的ThreadPoolExecutor
public static ExecutorService newCachedThreadPool() {

return new ThreadPoolExecutor(2, Integer.MAX_VALUE,

60L, TimeUnit.SECONDS,

new SynchronousQueue<Runnable>());//默认队列容量为Integer.MAX_VALUE

}

public static ExecutorService newLimitThreadPoll() {
return new ThreadPoolExecutor(2, 5,

0l, TimeUnit.SECONDS,

// new ArrayBlockingQueue<Runnable>(5));
new LinkedBlockingQueue<>(5),
// new ThreadPoolExecutor.AbortPolicy()); //异常中止
// new ThreadPoolExecutor.CallerRunsPolicy()); //在当前线程直接执行
// new ThreadPoolExecutor.DiscardPolicy()); //空实现,跳过任务
new ThreadPoolExecutor.DiscardOldestPolicy()); //移除队头,由pool执行
}


}

更多相关文章

  1. 线程“main”中的异常java.lang.RuntimeException:无法编译的源代
  2. Java多线程wait和notify协作,按序打印abc
  3. Java:在特定队列大小之后,以提交方式阻塞提交的ExecutorService。
  4. Java多线程系列八——volatile和ThreadLocal
  5. java线程池深入二
  6. Java Executor多线程框架
  7. java线程--volatile实现可见性
  8. Maven:主线程中的NoClassDefFoundError
  9. java线程池使用场景和使用方法较详细文摘

随机推荐

  1. android多框架实现短视频应用、3D手势旋
  2. 自定义Dialog的几种实现方式
  3. Android开发人员必备的10 个开发工具
  4. Android(安卓)Audio代码分析13 - AudioTr
  5. android 发布时去除Log
  6. AVD那些事儿
  7. android关于installLocation
  8. Android常见问题及讨论(10-15)
  9. 【Android 开发】:UI控件之拖动条控件 See
  10. Android的常用传感器