线程池
工具类:
- 缓冲线程池:Executors.newCachedThreadPool();
可以根据新加入任务自动调整线程池大小,没有任务队列
源码:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>()); // 无容量的任务队列
}
- 固定容量的线程池:Executors.newFixedThreadPool(3);
创建固定大小的线程池,新任务添加到任务队列
源码:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()); // 无界的任务队列
- 定义只有一个线程的线程池:Executors.newSingleThreadExecutor();
源码:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
线程池创建方法
1.工具类,不推荐
Executors 返回线程池对象的弊端如下:
FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE ,可能堆积大量的请求,从而导致OOM。
CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。
所有Executors返回的线程池类型,都是由WhreadPoolExecutor实现
2.ThreadPoolExecutor类,推荐
构造方法:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue
线程池的启动策略:
1、线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也
不会马上执行它们。
2、当调用 execute() 方法添加一个任务时,线程池会做如下判断:
a. 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
b. 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。
c. 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行这
个任务;
d. 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常,告
诉调用者“我不能再接受任务了”。
3、当一个线程完成任务时,它会从队列中取下一个任务来执行。
4、当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于
corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。
Callable 实现线程池Demo
ExecutorService pool = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
long statr = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
Future<Integer> result= pool.submit(new Call(i)); // 线程的返回值类型为Future<>;
try {
System.out.println(result.get(1000, TimeUnit.SECONDS)); // 取出返回值
} catch (InterruptedException | ExecutionException | TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
submit() 和 excute()方法的区别
- excute()传入实现了Runnable接口的对象,submit传入实现了Callable<>接口的对象,泛型为返回值类型
- excute()没有返回值, submit()返回一个Future<>类型的值,泛型为传入Callable的返回值
自定义线程池ThreadPoolExcutExecutor类的参数含义
/**参数含义
* corePoolSize 核心线程池大小
* maximumPoolSize 最大线程池大小
* keepAliveTime 当线程数大于核心时,此为终止前多余的空闲等待新任务最长间。
* TimeUnit keepAliveTime的时间单位
* workQueue 阻塞队列
*
*/