一、概述
Java线程的创建与销毁需要一定的开销,如果我们为每一个任务创建一个新线程来执行,这些线程的创建与销毁将消耗大量的计算资源。同时,
为每一个任务创建一个新线程来执行,这种策略可能会使处于高负荷状态的应用最终崩溃。Java的线程既是工作单元,也是执行机制。从JDK 5开始,把工作单元与执行机制分离开来。工作单元包括Runnable和Callable,而执行机制由Executor框架提供。
4.2 各工作队列
为每一个任务创建一个新线程来执行,这种策略可能会使处于高负荷状态的应用最终崩溃。Java的线程既是工作单元,也是执行机制。从JDK 5开始,把工作单元与执行机制分离开来。工作单元包括Runnable和Callable,而执行机制由Executor框架提供。
二、框架组成与主要接口
Executor框架主要由3大部分组成如下。
涉及的主要接口和实现
- ·任务。包括被执行任务需要实现的接口:Runnable接口或Callable接口。
- ·任务的执行。包括任务执行机制的核心接口Executor,以及继承自Executor的ExecutorService接口。Executor框架有两个关键类实现了ExecutorService接口(ThreadPoolExecutor和ScheduledThreadPoolExecutor)。
- ·异步计算的结果。包括接口Future和实现Future接口的FutureTask类。
三、线程池工作原理:
阅读ThreadPoolExecutor源码:在该类中维护了两个重要的变量(当然还有其他):
- private final BlockingQueue<Runnable> workQueue;
- private final HashSet<Worker> workers = new HashSet<Worker>();
结合上面两个属性来分析一下,线程池工作的几种情况,可以参看下面贴的execute()方法源码同步理解。
1. 当前worker数量
低于corePoolSize,直接创建新的work执行任务
2. 当前worker数量已达corePoolSize,将任务增加到workQueue;
3. 当前worker数量已达maxSize,且
workQueue中不能继续添加任务,则拒绝任务。
4. 线程池中的空闲worker循环从
workQueue中获取任务执行,当没有任务可以执行,线程的空闲时间超过keepAliveTime则线程被回收。
execute方法源码:
/**
* Executes the given task sometime in the future. The task
* may execute in a new thread or in an existing pooled thread.
*
* If the task cannot be submitted for execution, either because this
* executor has been shutdown or because its capacity has been reached,
* the task is handled by the current {@code RejectedExecutionHandler}.
*
* @param command the task to execute
* @throws RejectedExecutionException at discretion of
* {@code RejectedExecutionHandler}, if the task
* cannot be accepted for execution
* @throws NullPointerException if {@code command} is null
*/
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
四、各线程池与工作队列的特点:
4.1 各线程池
- FixedThreadPool,提供固定数量的线程数的线程池,适用于限制线程数量,服务器负载较重的场景。
- SingleThreadExecutor,单线程的线程池,适用于保证任务顺序执行且不会同时有多个活动线程的场景。
- CachedThreadPool, corePoolSize=0,根据需要创建线程,线程的数量大小无界,适用于执行很多短期异步或者负载轻的场景。(以上仨继承自ThreadPoolExecutor,以下俩继承自ScheduledThreadPoolExecutor)
- ScheduledThreadPoolExecutor,线程数量固定,适用于需要多个后台线程执行周期任务,且后台线程数量需要限制。
- SingelThreadScheduledExecutor,单后台线程执行周期任务,适用于保证执行顺序的场景。
- LinkedBlockingQueue, 无界队列(容量为Integer.MAX_VALUE), FixedThreadPool和SingleThreadExecutor采用LinkedBlockingQueue作为workQueue
- SynchronousQueue,无容量阻塞队列,每个插入操作必须等待一个线程的移除操作,cachedThreadPool通过使用SynchronousQueue将任务传递给空闲的线程。
- DelayedWorkQueue,无界队列,ScheduledThreadPoolExecutor在DelayedWorkQueue获取可以执行的任务。
学习代码
package com.vip.vcp.ryan.zhaunzheng.concurrency.leExecutors;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
/**
* 请简要描述Executor框架中提供的线程池的工作原理,并列举各线程池、工作队列类型及其特点。
* Created by ryan01.peng on 2017/6/19.
*/
public class LeExecutors {
public static void main(String[] args) {
// testFixedThreadPool();
// testCachedThreadPool();
// testSingleThreadExecutor();
testScheduledThreadPool();
}
private static void testFixedThreadPool(){
ExecutorService executorService1 = java.util.concurrent.Executors.newFixedThreadPool(5);
for (int i = 0; i <10 ; i++) {
executorService1.submit(new RunnableTask(i));
}
}
private static void testSingleThreadExecutor(){
ExecutorService executorService2 = Executors.newSingleThreadExecutor();
for (int i = 0; i <10 ; i++) {
executorService2.submit(new RunnableTask(i));
}
}
private static void testCachedThreadPool(){
ExecutorService executorService3 = Executors.newCachedThreadPool();
List<Future<String>> futureList = new ArrayList<>();
for (int i = 0; i <10 ; i++) {
Future<String> future= executorService3.submit(new CallableTask(i));
futureList.add(future);
}
System.out.println("executorService3线程池循环提交任务结束");
for (Future<String> future : futureList) {
try {
while (!future.isDone());//任务没有返回之前保持等待
System.out.println(future.get());
}catch (InterruptedException e){
e.printStackTrace();
}catch (Exception ex){
ex.printStackTrace();
}finally {
executorService3.shutdown();
}
}
}
private static void testScheduledThreadPool(){
ExecutorService executorService4 = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 10; i++) {
executorService4.submit(new ScheduleTask(i));
}
}
}
/**
* 无返回的可执行任务
*/
class RunnableTask implements Runnable{
private int id;
public RunnableTask(int id) {
this.id = id;
}
public RunnableTask() {
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"::RunnableTask -- "+id+" -- 的call方法被调用!!");
}
}
/**
* 有返回的可执行任务
*/
class CallableTask implements Callable<String > {
private int id;
public CallableTask(int id) {
this.id = id;
}
public CallableTask() {
}
@Override
public String call() throws Exception {
Thread.sleep(500);
return Thread.currentThread().getName()+"::CallableTask -- "+id+" -- 的call方法被调用!!";
}
}
class ScheduleTask implements Runnable{
private int id;
public ScheduleTask(int id) {
this.id = id;
}
@Override
public void run() {
// if (System.currentTimeMillis()%10 == 0){
System.out.println(Thread.currentThread().getName()+" :: 定时任务"+id+"被调用啦!");
try {
Thread.sleep(2000); //模拟一个任务执行需要两秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" :: 定时任务"+id+"执行完毕啦!");
// }
}
}
参考资料:
3. Java并发编程的艺术 第十章