目录
一、线程池介绍
二、任务
1. 常用的任务相关接口与类
2. FutureTask的实现
三、线程池的使用
1. 概述
2. 创建线程池
如果不使用线程池,编程人员则需要为每一个不在本线程执行的任务新建一个线程,线程的创建开销很大,且如果线程数量远大于cpu核数则频繁的上下文切换会导致程序执行效率大幅降低。
线程池维护了一个任务队列和若干线程,它自动地将任务分配给池中的线程执行,编程人员只需要将新的任务提交给线程池即可。线程池会按照预先设定好的参数决定维护几个线程,什么时候新建线程,什么时候销毁线程。
一般来说,为线程池提供一个Rannable,就会有一个线程调用run方法。当run方法退出时,这个线程不会死亡,而是留在池中为下一个请求提供服务。
线程池会维护一个任务队列,调用线程池的submit方法可以将Runnable或者Callable提交给任务队列,submit会立即返回一个Future对象,通过这个Future对象可以获取结果或取消任务。
/* Runnable接口 */
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
/* Callabel接口 */
@FunctionalInterface
public interface Callable {
V call() throws Exception;
}
public interface Future {
/* 如果任务还没执行则取消,如果正在执行且mayInterruptIfRunning为真则中断线程 */
boolean cancel(boolean mayInterruptIfRunning);
/* 任务是否被取消 */
boolean isCancelled();
/* 任务是否执行完毕 */
boolean isDone();
/* 阻塞直到任务执行结束,获取结果 */
V get() throws InterruptedException, ExecutionException;
/* 同上,但超时抛出TimeoutException异常 */
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
可以通过FutureTask类执行Callable,示例代码如下:
Callable task = ...;
var futureTask = new FutureTask(task);
var t = new Thread(futureTask); // 因为FutureTask实现了Runnable接口
t.start();
. . .
Integer result = futureTask.get();
主要的数据成员:
run() 方法实现如下:
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread())) // 初始化runner,失败则返回
return;
try {
Callable c = callable;
if (c != null && state == NEW) {
V result;
boolean ran; // 是否顺利执行(无异常)
try {
result = c.call(); // 执行任务
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex); // 产生异常,将outcome设置为异常,并更新状态
}
if (ran)
set(result); // 没有异常,将outcome设置为结果,并更新状态
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
JDK提供了一套Executor框架来方便开发人员使用线程池,它的核心成员如下:
以上成员均在java.util.concurrent包中,是JDK并发包的核心类。其中ThreadPoolExector表示一个线程池,Executors类是一个工厂类,通过newXXX静态方法可以返回不同设定的ThreadPoolExecutor或ScheduledThreadPoolExecutor线程池对象。
Executors类的newXXX方法返回ExecutorService或ScheduledExecutorService对象(分别由ThreadPoolExecutor和ScheduledThreadPoolExecutor实现),开发人员调用execute方法提交Runnable任务,调用submit方法提交Callable任务并通过返回的Future对象获取返回值,调用shutdown方法关闭服务(线程池处理完已提交任务后,不再接受新任务),调用schedule方法延迟执行或定期执行任务。
我们一般通过调用Executors类的静态方法创建线程池,而不是直接去new ThreadPoolExecutor或new ScheduledThreadPoolExecutor,ThreadPoolExecutor的构造方法如下(比较复杂,简单介绍):
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler);
其中corePoolSize指定线程池中线程的数量,maximumPoolSize指定线程池中最大线程数量,keepAliveTime指定当线程池线程数超过corePoolSize时多余的线程的存活时间,unit为keepAliveTime的单位,workQueue为任务队列(保存被提交但尚未执行的任务,是一个BlockingQueue接口的对象,内置有多种不同的类型,比如:有界的ArrayBlockingQueue、无界的LinkedBlockingQueue,能处理优先级的PriorityBlockingQueue,没有容量直接提交的SynchronousQueue),threadFactory是线程工厂(用来创建线程,是一个接口,有一个newThread方法),handler指定拒绝策略(任务过多时如何处理)。
Executors中常用的静态工厂方法如下: