为什么要用线程池?原因很简单,性能好,而且不用自己费心费力的管理线程
1、线程池基本说明及定义
从JDK 1.5开始,添加了Executors工具类,这个类定义了Executor、ExecutorService、ScheduledExecutorService、ThreadFactory、Calable类的工厂和实用方法。在Executors类的源码注释中,这样写道:
该类包含:
1. 提供能够创建并返回设置了常用配置的ExecutorService的方法2. 提供能够创建并返回设置了常用配置的ScheduledExecutorService的方法
3. 提供能够创建并返回一个经过包装(禁用了特定的实现方法,仅仅暴露ExecutorService的实现)的ExecutorService方法
4. 提供能够创建并返回能修改新建线程状态的ThreadFactory的方法
5. 提供能够创建并返回非闭包形式的Callable,因此能够在需要Callable的执行方法中使用
Executors类提供4种线程池:
1. newFixedThreadPool:创建固定大小的线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
相比下面将要介绍的newCachedThreadPool,newFixedThreadPool
可控制线程最大并发数,当线程池中的线程数达到其设定大小时,其余新创建的线程会在LinkedBlockingQueue队列中等待。当线程池中的某个线程失败而终止时,新的线程会代替它执行剩下的任务。线程池中的线程只有在显式调用shutdown函数时才会退出线程池
2. newCachedThreadPool:创建
可缓存线程池,当线程池中的线程空闲时间超过60s,便会终止该空闲线程并从缓存线程池中移除
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
该线程池可根据需要,创建新线程,从上面代码中可以看出,其线程数最大可为Integer.MAX_VALUE,如果控制不好,使用该线程池可能导致线程创建过多,反而影响性能。因此,可以注意一下该线程池的使用场景:对于大量短暂异步任务的程序来说,使用该线程池能够大大提高性能
3. newSingleThreadExecutor,创建单线程的线程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
当该单线程在shutdown之前由于失败而终止时,将会有新的线程来代替它执行剩下任务。加入到该线程池里的线程会按顺序执行,一个时刻保证
只有一个线程在运行
4. newScheduledThreadPool,创建
固定大小且能够执行
定时或周期性任务的线程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
2、线程池使用
使用过程很简单,下面以newFixedThreadPool为例:创建一个大小为3的一个线程池,一共10个线程任务,每个任务会输出当前执行线程的名称,然后睡眠500ms(睡眠的目的是使结果更清晰)public class TestNewFixedThreadPool {
public static void main(String[] args) {
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; ++i) {
newFixedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
结果:
pool-1-thread-1
pool-1-thread-3
pool-1-thread-2
pool-1-thread-2
pool-1-thread-1
pool-1-thread-3
pool-1-thread-1
pool-1-thread-2
pool-1-thread-3
pool-1-thread-3
从输出结果可以看出,在线程池中一共有3个线程,原因是该线程池控制了最大并发线程数为3。当使用newCachedThreadPool时,由于存在睡眠时长,很有可能会有10个线程来执行