结构一个线程池为什么需要几个参数?假如制止线程池呈现OOM?Runnable
和Callable
的区别是什么?本文将对这些问题一一解答,同时还将给出利用线程池的常见场景和代码片断。
基本常识
Executors建设线程池
Java中建设线程池很简朴,只需要挪用Executors
中相应的便捷要领即可,好比Executors.newFixedThreadPool(int nThreads)
,可是便捷不只埋没了巨大性,也为我们埋下了潜在的隐患(OOM,线程耗尽)。
Executors
建设线程池便捷要领列表:
要领名 | 成果 |
---|---|
newFixedThreadPool(int nThreads) | 建设牢靠巨细的线程池 |
newSingleThreadExecutor() | 建设只有一个线程的线程池 |
newCachedThreadPool() | 建设一个不限线程数上限的线程池,任何提交的任务都将当即执行 |
小措施利用这些快捷要领没什么问题,对付处事端需要恒久运行的措施,建设线程池应该直接利用ThreadPoolExecutor
的结构要领。没错,上述Executors
要领建设的线程池就是ThreadPoolExecutor
。
ThreadPoolExecutor结构要领
Executors
中建设线程池的快捷要领,实际上是挪用了ThreadPoolExecutor
的结构要领(按时任务利用的是ScheduledThreadPoolExecutor
),该类结构要领参数列表如下:
// Java线程池的完整结构函数 public ThreadPoolExecutor( int corePoolSize, // 线程池恒久维持的线程数,纵然线程处于Idle状态,也不会接纳。 int maximumPoolSize, // 线程数的上限 long keepAliveTime, TimeUnit unit, // 高出corePoolSize的线程的idle时长, // 高出这个时间,多余的线程会被接纳。 BlockingQueue<Runnable> workQueue, // 任务的列队行列 ThreadFactory threadFactory, // 新线程的发生方法 RejectedExecutionHandler handler) // 拒绝计策
竟然有7个参数,很无奈,结构一个线程池确实需要这么多参数。这些参数中,较量容易引起问题的有corePoolSize
, maximumPoolSize
, workQueue
以及handler
:
corePoolSize
和maximumPoolSize
配置不妥会影响效率,甚至耗尽线程;workQueue
配置不妥容易导致OOM;handler
配置不妥会导致提交任务时抛出异常。正确的参数配置方法会在下文给出。
线程池的事情顺序
If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than queuing.
If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread.
If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, the task will be rejected.
corePoolSize -> 任务行列 -> maximumPoolSize -> 拒绝计策
Runnable和Callable
可以向线程池提交的任务有两种:Runnable
和Callable
,二者的区别如下:
void Runnable.run()
, V Callable.call() throws Exception
Callable
答允有返回值Callable
答允抛出异常。Callable
是JDK1.5时插手的接口,作为Runnable
的一种增补,劳务派遣管理系统,答允有返回值,答允抛出异常。
三种提交任务的方法:
提交方法 | 是否体贴返回功效 |
---|---|
Future<T> submit(Callable<T> task) |
是 |
void execute(Runnable command) |
否 |
Future<?> submit(Runnable task) |
否,固然返回Future,可是其get()要领老是返回null |
如何正确利用线程池
制止利用无界行列
不要利用Executors.newXXXThreadPool()
快捷要领建设线程池,因为这种方法会利用无界的任务行列,为制止OOM,我们应该利用ThreadPoolExecutor
的结构要领手动指定行列的最大长度:
ExecutorService executorService = new ThreadPoolExecutor(2, 2, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(512), // 利用有界行列,制止OOM new ThreadPoolExecutor.DiscardPolicy());
明晰拒绝任务时的行为