缘由
由于涉及与操作系统的交互,构建一个新的线程是有一定代价的。如果程序中需要大量生命周期很短的线程,应该使用线程池。
线程池
一个线程池(thread pool)中包含许多准备运行的空闲线程,将Runnable
对象交给线程池,就会有一个线程调用run
方法。当run
方法退出时,线程不会死亡,而是在线程池中准备为下一个请求提供服务。
使用线程池还可以减少并发线程的数目。创建大量线程会大大降低性能甚至使虚拟机崩溃,如果算法会创建许多线程,应该使用线程数固定的线程池,来限制并发线程的总数。
构建线程池
执行器(Executor)类的静态工厂方法能用来构建线程池。
方法 | 描述 |
---|---|
newCachedThreadPool | 必要时创建新进程;空闲线程会被保留60秒 |
newFixedThreadPool | 该池包含固定数量的线程;空闲线程会一直被保留 |
newSingleThreadExecutor | 只有一个线程的池,该线程顺序执行每一个提交的任务 |
newScheduledThreadPool | 用于预定执行而构建的固定线程池,替代java.util.Timer |
newSingleThreadScheduledExecutor | 用于预定执行而构建的单线程池 |
newCachedThreadPool
构建了一个线程池,对于每个任务,如果有空闲线程可用,立即让它执行任务,如果没有可用的空闲线程,则创建一个新线程。
newFixedThreadPool
构建一个具有固定大小的线程池,如果提交的任务多于空闲的线程数,那么把得不到服务的任务放到队列中。当其他任务完成以后再运行它们。
newSingleThreadPool
是大小为1的线程池,由一个线程执行提交的任务,一个接着一个。
上述三个方法返回实现了ExecutorService
接口的ThreadPoolExecutor
类的对象。而以下三个方法都可以将一个Runnable
对象或Callable
对象提交给ExecutorService
:
Future<?> submit(Runnable task)
该方法返回的是Future<?>
,可以调用isDone
、cancel
或isCancelled
,但是使用get
方法时只能得到null
。
Future<T> submit(Runnable task, T result)
该方法也提交一个Runnable
,并且Future
的get
方法在完成的时候返回指定的result对象。
Future<T> submit(Callable<T> task)
该方法提交一个Callable
,并且返回的Future
对象将在计算结果准备好的时候得到它。
使用线程池要做的事
- 调用
Executors
类中静态的方法newCachedThreadPool
或newFixedThreadPool。 - 调用
submit
方法提交Runnable
或Callable
对象。 - 如果想要取消一个任务,或如果提交
Callable
对象,那就要保存好返回的Future
对象。 - 当不再提交任何任务时,调用
shutdown
。
参考:《Java核心技术 卷1》 第14章 并发