PS: Spring ThreadPoolTaskExecutor vs Java Executorservice cachedthreadpooljava
引用spring
【轰隆隆】 的 Java自带的线程池ThreadPoolExecutor详细介绍说明和实例应用
来源:YidingHe's Blogide
从 Java 5 开始,Java 提供了本身的线程池。线程池就是一个线程的容器,每次只执行额定数量的线程。 java.util.concurrent.ThreadPoolExecutor 就是这样的线程池。它很灵活,但使用起来也比较复杂,本文就对其作一个介绍。函数
首先是构造函数。以最简单的构造函数为例:this
public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue)
public ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) 线程看起来挺复杂的。这里介绍一下。orm
corePoolSize 指的是保留的线程池大小。
maximumPoolSize 指的是线程池的最大大小。
keepAliveTime 指的是空闲线程结束的超时时间。
unit 是一个枚举,表示 keepAliveTime 的单位。
workQueue 表示存听任务的队列。对象咱们能够从线程池的工做过程当中了解这些参数的意义。线程池的工做过程以下:blog
一、线程池刚建立时,里面没有一个线程。任务队列是做为参数传进来的。不过,就算队列里面有任务,线程池也不会立刻执行它们。接口
二、当调用 execute() 方法添加一个任务时,线程池会作以下判断:
a. 若是正在运行的线程数量小于 corePoolSize,那么立刻建立线程运行这个任务;
b. 若是正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。
c. 若是这时候队列满了,并且正在运行的线程数量小于 maximumPoolSize,那么仍是要建立线程运行这个任务;
d. 若是队列满了,并且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常,告诉调用者“我不能再接受任务了”。
三、当一个线程完成任务时,它会从队列中取下一个任务来执行。
四、当一个线程无事可作,超过必定的时间(keepAliveTime)时,线程池会判断,若是当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。因此线程池的全部任务完成后,它最终会收缩到 corePoolSize 的大小。
这样的过程说明,并非先加入任务就必定会先执行。假设队列大小为 10,corePoolSize 为 3,maximumPoolSize 为 6,那么当加入 20 个任务时,执行的顺序就是这样的:首先执行任务 一、二、3,而后任务 4~13 被放入队列。这时候队列满了,任务 1四、1五、16 会被立刻执行,而任务 17~20 则会抛出异常。最终顺序是:一、二、三、1四、1五、1六、四、五、六、七、八、九、十、十一、十二、13。下面是一个线程池使用的例子:
public static void main(String[] args) {
BlockingQueue queue = new LinkedBlockingQueue();
ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 1, TimeUnit.DAYS, queue);
for (int i = 0; i < 20; i++) {
executor.execute(new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(String.format("thread %d finished", this.hashCode()));
}
});
}
executor.shutdown();
}对这个例子的说明以下:
一、BlockingQueue 只是一个接口,经常使用的实现类有 LinkedBlockingQueue 和 ArrayBlockingQueue。用 LinkedBlockingQueue 的好处在于没有大小限制。这样的话,由于队列不会满,因此 execute() 不会抛出异常,而线程池中运行的线程数也永远不会超过 corePoolSize 个,keepAliveTime 参数也就没有意义了。
二、shutdown() 方法不会阻塞。调用 shutdown() 方法以后,主线程就立刻结束了,而线程池会继续运行直到全部任务执行完才会中止。若是不调用 shutdown() 方法,那么线程池会一直保持下去,以便随时添加新的任务。
到这里对于这个线程池还只是介绍了一小部分。ThreadPoolExecutor 具备很强的可扩展性,不过扩展它的前提是要熟悉它的工做方式。
java.util.concurrent.ThreadPoolExecutor 类提供了丰富的可扩展性。你能够经过建立它的子类来自定义它的行为。例如,我但愿当每一个任务结束以后打印一条消息,但我又没法修改任务对象,那么我能够这样写:
ThreadPoolExecutor executor = new ThreadPoolExecutor(size, maxSize, 1, TimeUnit.DAYS, queue) {
@Override
protected void afterExecute(Runnable r, Throwable t) {
System.out.println("Task finished.");
}
};
除了 afterExecute 方法以外,ThreadPoolExecutor 类还有 beforeExecute() 和 terminated() 方法能够重写,分别是在任务执行以前和整个线程池中止以后执行。
除了能够添加任务执行先后的动做以外, ThreadPoolExecutor 还容许你自定义当添加任务失败后的执行策略。你能够调用线程池的 setRejectedExecutionHandler() 方法,用自定义的 RejectedExecutionHandler 对象替换现有的策略。 ThreadPoolExecutor 提供 4 个现有的策略,分别是:
ThreadPoolExecutor.AbortPolicy:表示拒绝任务并抛出异常
ThreadPoolExecutor.DiscardPolicy:表示拒绝任务但不作任何动做
ThreadPoolExecutor.CallerRunsPolicy:表示拒绝任务,并在调用者的线程中直接执行该任务
ThreadPoolExecutor.DiscardOldestPolicy:表示先丢弃任务队列中的第一个任务,而后把这个任务加进队列。
这里是一个例子:
ThreadPoolExecutor executor = new ThreadPoolExecutor(size, maxSize, 1, TimeUnit.DAYS, queue);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
除此以外,你也能够经过实现 RejectedExecutionHandler 接口来编写本身的策略。下面是一个例子:
ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 1, TimeUnit.SECONDS, queue,
new RejectedExecutionHandler() {
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println(String.format("Task %d rejected.", r.hashCode()));
}
}
);
【轰隆隆】推荐: