深入浅出java线程池
前言
Java1.5后引入的Executor框架的最大优点是把任务的提交和执行解耦,只需把Task描述清楚,然后提交即可。至于这个Task是怎么被执行的,被谁执行的,什么时候执行的,就全部交给线程池管理。
小案例
废话不多说,先来一个小小案例。
import java.util.concurrent.*;
public class ExecutorTest {
static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws Exception {
Future<String> future = executor.submit(new Task());
String result = future.get();
System.out.println(result);
}
static class Task implements Callable<String> {
@Override
public String call() throws Exception {
TimeUnit.SECONDS.sleep(10);
return "A";
}
}
}
代码很简单:初始化一个线程池,提交一个任务,主线程的future.get()会阻塞线程直到任务执行完成。
Executor框架成员
线程池实现框架中包含了一堆实现类,它们之间的关系如下,只有了解了各个类之间的关系,才能方便我们更好的理解线程池的实现。

从图中可以看到Executor、ExecutorService、ScheduledExecutorService定义线程池接口,ThreadPoolExecutor和ScheduledThreadPoolExecutor是线程池的实现,前者是一个普通的线程池,后者一个定期调度的线程池,Executors是辅助工具,用以帮助我们快速定义线程池。
参数
在初始化线程池时,不同的应用场景中,对参数的选择是很重要的,先来看看线程池的各个参数的含义:
1. workQueue:用来保存等待执行的任务的阻塞队列。
2. corePoolSize:线程池的基本大小。当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使有其它空闲的线程,直到线程数达到corePoolSize时就不再创建,这时会把提交的新任务放到阻塞队列。如果调用了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程。
3. maximumPoolSize:线程池允许创建的最大线程数。如果阻塞队列满了,并且已经创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。
4. threadFactory:创建线程的工厂。可以通过自定义线程工厂给每个线程设置有意义的名称。如guava提供的ThreadFactoryBuilder。
new ThreadFactoryBuilder().setNameFormat("XX-task-%d").build();
5. rejectedExecutionHandler:饱和策略。当阻塞队列满了且没有空闲的工作线程,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略在默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。不过,线程池提供了4种策略:
- AbortPolicy:直接抛出异常。
- CallerRunsPolicy:只用调用者所在的线程来运行任务。
- DiscardOldestPolicy:丢弃阻塞队列中最近的一个任务,并执行当前任务。
- DiscardPolicy:直接丢弃。
当然,也可以根据应用场景来实现RejectedExecutionHandler接口自定义饱和策略,如记录日志或持久化存储不能处理的任务。
6. keepAliveTime:线程活动保持时间。指工作线程空闲后,继续保持存活的时间。默认情况下,这个参数只有在线程数大于corePoolSize时才起作用。所以,如果任务很多,且每个任务的执行时间比较短,可以调大keepAliveTime,提高线程的利用率。
在初始化线程池时,对阻塞队列的选择也很重要,jdk中提供了以下几个阻塞队列:
- ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO原则对元素进行排序。
- LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序元素,吞吐量通常要高于ArrayBlockingQuene。
- SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene。
- priorityBlockingQuene:具有优先级的无界阻塞队列。
Exectors
Exectors是java线程池的工厂类,通过它可以快速初始化一个符合业务需求的线程池,主要提供了以下几种便捷的方式:
1. newFixedThreadPool:创建一个指定工作线程数的线程池,其中参数corePoolSize和maximumPoolSize相等,阻塞队列基于LinkedBlockingQuene。
它是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
2. newCachedThreadPool:创建一个可缓存工作线程的线程池(工作线程默认存活时间1分钟)。该线程池有以下特点:
This chapter requires login to view full content. You are viewing a preview.
Login to View Full Content