假设我们编写了一个Servlet应用,当用户通过浏览器发起一个请求到达我们服务器时,传统的Servlet应用一般针对一个用户请求创建一个线程去执行请求,等到请求执行完毕后,再销毁线程。这种设计在用户量几百或者几千的情况下一般不会有什么大问题,但是如果我们的用户量上达几万甚至几十万几百万,频繁的创建、销毁线程,将会给服务器带来巨大的开销,甚至会出现OOM(Out Of Memory)异常。因此,为了节省资源的消耗,提高资源的利用率,引出了线程池化技术。
线程池会维护若干线程,等待任务的到来,避免重复创建、销毁线程造成的消耗,提高任务的响应速度,不需要创建线程就可以立即执行任务,使用线程池可以进行统一的分配、调优和监控,避免无节制的创建线程,提高系统的稳定性。
当然,线程池也并非十全十美,它也有力不从心的场景,例如:线程池适合生命周期较短的任务,不适合耗时较长的任务;线程池无法设置任务的优先级,也无法单独启动或者终止某个线程。
现在,我们对比一下线程池执行任务和创建线程执行任务的优势。在ThreadTest中,我们声明了一个线程安全的list,并创建10000个线程并发往list添加随机值,最后我们等待所有线程执行完毕,打印程序的执行时间和list的大小。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
Long start = System.currentTimeMillis();
final Random random = new Random();
final List<Integer> list = Collections.synchronizedList(new ArrayList<>());
final List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
Thread thread = new Thread(() -> list.add(random.nextInt()));
thread.start();
threads.add(thread);
}
for (Thread thread : threads) {
thread.join();
}
System.out.println("时间:" + (System.currentTimeMillis() - start));
System.out.println("大小:" + list.size());
}
}
执行结果:
时间:882 大小:10000
可以看到list的长度为10000,程序执行了882毫秒。
下面,我们用线程池的方式来执行相同的逻辑,我们声明一个线程池executorService,并往线程池中提交10000个任务,每个任务都向list添加一个随机值:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ThreadPoolTest {
public static void main(String[] args) throws InterruptedException {
Long start = System.currentTimeMillis();
final Random random = new Random();
final List<Integer> list = Collections.synchronizedList(new ArrayList<>());
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10000; i++) {
executorService.execute(() -> list.add(random.nextInt()));
}
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.DAYS);
System.out.println("时间:" + (System.currentTimeMillis() - start));
System.out.println("大小:" + list.size());
}
}
执行结果:
时间:52 大小:10000
可以看到线程池的执行时间相比创建线程,大大缩短。
下面,我们就从源码的角度,来剖析线程池的工作原理。ThreadPoolExecutor是java.util.concurrent包下提供的线程池实现类,下图是ThreadPoolExecutor类的继承关系,我们将从上至下逐个分析ThreadPoolExecutor的父类:Executor、ExecutorService、AbstractExecutorService。

我们先来看下Executor接口定义:
public interface Executor {
void execute(Runnable command);
}
Executor允许我们提交若干待执行的任务,我们不再像以前一样用new Thread(new RunnableTask()).start()的方式启动一个线程去执行RunnableTask的run()方法,取而代之的是用Executor的实现类去执行,比如:
Executor executor = anExecutor; executor.execute(new RunnableTask1()); executor.execute(new RunnableTask2()); ...
Executor提供了一种新的方式,我们只需提交任务,Executor自身负责如何调度线程来执行任务。Executor并不要求任务的执行必须是异步的,也可以在提交完任务后,同步执行任务:
class DirectExecutor implements Executor {
public void execute(Runnable r) {
r.run();
}
}
通常情况下,Executor会将提交过来的任务放在另一个线程执行,而不是通过调用线程来执行:
class ThreadPerTaskExecutor implements Executor {
public void execute(Runnable r) {
new Thread(r).start();
}
}
一些Executor接口的实现在调度线程执行任务时会添加一些限制,比如我们可以以代理模式的思想来封装Executor:
class SerialExecutor implements Executor {
final Queue<Runnable> tasks = new ArrayDeque<Runnable>();
final Executor executor;
Runnable active;
SerialExecutor(Executor executor) {
this.executor = executor;
}
public synchronized void execute(final Runnable r) {
tasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (active == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((active = tasks.poll()) != null) {
executor.execute(active);
}
}
}
在创建SerialExecutor对象时,会要求传入一个executor对象,执行任务并不是SerialExecutor对象本身,SerialExecutor并不执行任务,只是将任务缓存到队列tasks,而executor才是真正负责执行任务。
ExecutorService扩展了Executor,ExecutorService不但具备Executor执行任务的能力,我们还可以关闭ExecutorService,这将使ExecutorService拒绝接受新提交的任务。ExecutorService.submit(...)方法是基于Executor.execute(Runnable command)封装的,execute方法没有任何返回,而submit会返回一个Future对象,通过Future对象我们可以取消任务或者等待任务未来的执行结果。
public interface ExecutorService extends Executor {
/**
* 关闭线程池,调用此方法后不再接受新任务,但会处理线程池内
* 尚未完成的任务,如果线程池已经关闭,再次调用此方法将无事发生。
* 这个方法不会等待已提交但尚未完成的任务执行完毕,需要调用awaitTermination(long timeout, TimeUnit unit)
* 来等待。
*/
void shutdown();
/**
* 调用此方法会尝试停止所有正在运行的线程,比如:调用Thread.interrupt()
* 标记线程已中断,如果任务没有响应中断则线程无法停止。这个方法会返回尚未
* 执行的任务列表,它不会等待正在执行的任务执行完毕,需要调用awaitTermination(long timeout, TimeUnit unit)
* 来等待。
*/
List<Runnable> shutdownNow();
/**
* 判断线程池是否已关闭,true为关闭。
*/
boolean isShutdown();
/**
* 如果调用shutdown()或者shutdownNow()后,所有任务都已完成,则返回true
*/
boolean isTerminated();
/**
* 使当前调用此方法线程陷入阻塞,直到:
* 1.调用停止线程池方法后,完成所有任务。
* 2.阻塞超时。
* 3.调用此方法线程被中断。
*
* @param timeout 最大等待时长
* @param unit 时长单位
* @return 如果任务都执行完毕返回true,如果超时或中断则返回false
*/
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
/**
* 提交一个有返回值的任务后,将返回一个Future对象代表任务的运行结果,
* 可以调用Future.get()获得任务的执行结果,如果提交任务后想立即获得结果,
* 可以用:result = exec.submit(aCallable).get();这样的方式获得,
* 调用线程将陷入阻塞,直到线程池执行完任务,执行结果被放入到Future对象。
*
* @param task 待执行的任务。
* @param <T> 任务执行结果的类型。
* @return 返回Future对象,线程池执行完毕任务后,执行结果会被放入到Future对象。
*/
<T> Future<T> submit(Callable<T> task);
/**
* 提交一个可执行的任务和给定的执行结果,并返回一个Future对象代表该任务将来的运行结果,
* 如果任务成功执行,Future对象将返回我们给定的执行结果。
*
* @param task 待执行的任务
* @param result 任务执行完毕后的返回结果
* @param <T> 返回结果的类型
* @return 返回Future对象,线程池执行完毕任务后,执行结果会被放入到Future对象。
*/
<T> Future<T> submit(Runnable task, T result);
/**
* 提交待执行的任务并返回Future对象代表该任务未来的运行结果,如果任务执行成功,
* 调用Future.get()将返回null。
*
* @param task 待执行的任务
* @return 返回Future对象,代表该任务未来的运行结果。
*/
Future<?> submit(Runnable task);
}
我们可以用ExecutorService来模拟一个网络服务,用Executors.newFixedThreadPool(int)工厂方法生成的线程池中的线程来处理传入的网络请求:
class NetworkService implements Runnable {
private final ServerSocket serverSocket;
private final ExecutorService pool;
public NetworkService(int port, int poolSize)
throws IOException {
serverSocket = new ServerSocket(port);
pool = Executors.newFixedThreadPool(poolSize);
}
public void run() { // run the service
try {
for (;;) {
pool.execute(new Handler(serverSocket.accept()));
}
} catch (IOException ex) {
pool.shutdown();
}
}
}
class Handler implements Runnable {
private final Socket socket;
Handler(Socket socket) { this.socket = socket; }
public void run() {
// read and service request on socket
}
}
关闭ExecutorService分两个阶段,首先调用shutdown()拒绝再有新的任务提交,然后在必要的时候调用shutdownNow(),尝试中断正在执行的任务。
void shutdownAndAwaitTermination(ExecutorService pool) {
pool.shutdown(); // Disable new tasks from being submitted
try {
// Wait a while for existing tasks to terminate
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow(); // Cancel currently executing tasks
// Wait a while for tasks to respond to being cancelled
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
System.err.println("Pool did not terminate");
}
} catch (InterruptedException ie) {
// (Re-)Cancel if current thread also interrupted
pool.shutdownNow();
// Preserve interrupt status
Thread.currentThread().interrupt();
}
}
AbstractExecutorService是juc(java.util.concurrent)包下提供的ExecutorService接口的默认实现,在newTaskFor方法中返回FutureTask作为RunnableFuture接口的实现。可以看到不论是Runnable还是Callable类型的任务,都会被封装成RunnableFuture类型的任务来执行。
public abstract class AbstractExecutorService implements ExecutorService {
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
}
Java并发之ThreadPoolExecutor源码解析(一)
原文:https://www.cnblogs.com/beiluowuzheng/p/14393058.html