线程池是一种多线程的处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。
线程池的优点:
(1)线程的重用
线程的创建和销毁开销很大,而使用线程池的出现增强了线程的重用性。
(2)控制并发数
(3)线程池可以对线程进行管理
- Executor
最顶层接口,只定义了一个方法void execute(Runnable command)。
public interface Executor {
void execute(Runnable command);
}
execute方法没有返回值,原因是Runnable接口中的run方法没有返回值。
ThreadPoolExecutor类中实现了execute方法。
源代码:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
// 获取最新的线程池数据
int c = ctl.get();
// 比较线程池中的worker数量和线程池的核心容量
if (workerCountOf(c) < corePoolSize) {
// 如果线程池中的worker数量少,直接新增一个worker。如果添加成功,直接返回。
if (addWorker(command, true))
return;
// 如果添加失败,获取最新的线程池数据
c = ctl.get();
}
// 如果线程池仍在运行,则把任务放到阻塞队列中等待执行。
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 当任务成功放入队列时,如果recheck发现线程池已经不再运行了则从队列中把任务删除
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
用户向线程池提交任务以后,线程池的执行逻辑:
(1)如果当前woker数量小于核心容量,则新建一个woker并把当前任务分配给该woker线程,成功则返回。
(2)如果第1步失败,则尝试把任务放入阻塞队列,成功则返回。
(3)如果第2步失败,则比较当前woker数量和线程池的最大容量。如果当前woker数量小于线程池的最大容量,则新建一个woker并把当前任务分配给该woker线程,成功则返回。
(4)如果第3步失败,则调用拒绝策略处理该任务。
示例:
public class Test1_Executor implements Executor {
// 实现一个简单的execute方法
public void execute(Runnable command) {
new Thread(command).start();
}
public static void main(String[] args) {
Test1_Executor executor = new Test1_Executor();
executor.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + " - test executor");// 输出Thread-0 - test executor
}
});
}
}
2.ExecutorService
ExecutorService定义了很多方法,其中包括submit方法,这个方法有返回值(Future类型)。submit方法是一个重载方法,有参数类型为Runnable的,有参数类型为Callable的。
常见方法:void execute(Runnable command),<T> Future<T> submit(Callable<T> task),Future<?> submit(Runnable task),void shutdown(),List<Runnable> shutdownNow()。
3.AbstractExecutorService
抽象类,类中实现了一些方法直接供子类使用。
4.ThreadPoolExecutor
Future<V> -> RunnableFuture<V> -> FutureTask<V>
Runnable -> RunnableFuture<V>
FutureTask通过RunnableFuture间接实现了Runnable接口。每个Runnable通常会先包装成FutureTask,然后调用executor.execute(Runnable command)将其提交给线程池。
Future
未来结果,ExecutorService中的submit方法的返回值类型。
get无参方法 - 阻塞等待线程执行结束,获取结果。
get有参方法 - 阻塞固定时长,等待线程执行结束,获取结果;如果在阻塞的过程中,线程未执行结束,抛出异常。
isDone方法 - 查看线程是否执行结束。Callable
可执行接口,类似Runnable接口,也是一个可以启动线程的接口。Callable接口中定义的方法是call方法,call方法的作用和Runnable中的run方法相同,只是call方法有返回值并可以抛出异常。
Callable只能在ExecutorService中的submit方法中使用。
ExecutorService+Future+Callable示例:
public class Test2_Future {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
Future<String> future = executorService.submit(new Callable<String>() {
public String call() {
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("aaa");
return Thread.currentThread().getName() + " - test executor";
}
});
System.out.println(future);
System.out.println(future.isDone());// 查看线程是否执行结束, 即call方法是否执行结束
System.out.println(future.get());// 等待线程执行结束,获取call方法的返回值。
System.out.println(future.isDone());
}
}
- Executors
工具类型(类似于Arrays,Collections),可以快速地创建若干种线程池。例如:固定容量的,无限容量的,容量为1的线程池。 - 线程池的状态
线程池是一个进程级的重量级资源。默认它的生命周期和JVM一致。当线程池开启后,直到JVM关闭为止,是线程池的默认生命周期。
线程池状态:Running,ShuttingDown,Terminated
(1)Running:线程池正在执行。
(2)ShuttingDown:线程池正在关闭,优雅关闭(执行了shutdown方法)。一旦进入这个状态,线程池不会再接收新的任务(包括等待队列中的任务),只处理所有已接收的任务,处理完毕后,线程池自动关闭。
(3)Terminated - 线程池已经关闭。