ThreadPoolExecutor的构造方法如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
线程池中线程工作顺序:
如果正在执行总线程数小于corePoolSize,Executor 会优先创建新线程到线程池;
如果正在执行总线程数大于corePoolSize,Executor 通常会将线程请求加入workQueue;
如果线程请求不能被加入workQueue,Executor 会创建新线程,除非当前正在运行的线程数大于maximumPoolSize,这种情况下Executor会根据拒绝策略拒绝任务。
corePoolSize -> 任务队列 -> maximumPoolSize -> 拒绝策略
Executors提供了创建线程池的便捷方法,如:Executors.newFixedThreadPool(int nThreads),但此方法隐藏了线程池的复杂性,会埋下隐患(定义一个Integer.MAX_VALUE大小的LinkedBlockingQueue,导致OOM; 无上限地创建线程,导致线程耗尽)
创建线程池的便捷方法 | 功能 | 应用场景 | 注意事项 |
---|---|---|---|
Executors.newFixedThreadPool(10) | 创建固定大小的线程池,可控制最大并发数 | 适用于处理CPU密集型的任务,确保CPU在长期被工作线程使用的情况下,尽可能的少的分配线程,即适用执行长期的任务。 | 线程池大小最好依赖系统资源定义,如Runtime.getRuntime().availableProcessors() |
Executors.newSingleThreadExecutor() | 创建一个线程的线程池(corePoolSize和maximumPoolSize都为1) | 适用于串行执行任务的场景,一个任务一个任务地执行。 | |
Executors.newCachedThreadPool() | 创建可缓存线程池(corePoolSize=0,keepAliveTime=60s说明线程资源释放后可以缓存一分钟) | 用于并发执行大量短期的小任务 | 线程数无上限,如果任务处理速度 < 任务提交速度,就会不断创建新线程导致资源耗尽。所以该线程池适合执行时间较短的任务 |
Executors.newScheduledThreadPool(1) | 创建一个定制执行线程的线程池 | 周期性执行任务的场景,需要限制线程数量的场景 |
提交方式 | 释放关心返回结果 |
---|---|
Future |
是 |
void execute(Runnable command) | 否 |
Future |
否。会返回Future,不过get()得到的永远是null; |
Runnable和Callable的区别
1、方法签名不同。void Runnable.run(), V callable.call() throw Exception
2、Runnable 无返回值,Callable有返回值
3、Runnable不可抛出异常,Callable可以抛出异常
ExecutorService executorService = new ThreadPoolExecutor(2, 2,
0, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(512), // 使用有界队列,避免OOM
new ThreadPoolExecutor.DiscardPolicy());
拒绝策略 | 拒绝行为 |
---|---|
AbortPolicy(默认) | 抛出RejectedExecutionException异常 |
CallerRunsPolicy | 直接由任务提交线程执行该任务 |
DiscardOldestPolicy | 丢弃workQueue中最老的任务,尝试将新任务加入到workQueue中 |
DiscardPolicy | 什么都不做,直接丢弃新任务 |
答:如上文所述。
答:1、 线程内try-catch捕获处理;
2、submit Callable线程,通过Future.get()获取线程异常并处理。
3、重写ThreadPoolExecutor的afterExecute()方法,处理传递的异常引用。
class ExtendedExecutor extends ThreadPoolExecutor {
// 这可是jdk文档里面给的例子。。
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future<?>) {
try {
Object result = ((Future<?>) r).get();
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt(); // ignore/reset
}
}
if (t != null)
System.out.println(t);
}
}}
4、为工作线程设置UncaughtExceptionHandler,在uncaughtException中处理异常。
ExecutorService threadPool = Executors.newFixedThreadPool(1, r -> {
Thread t = new Thread(r);
t.setUncaughtExceptionHandler(
(t1, e) -> {
System.out.println(t1.getName() + "线程抛出的异常"+e);
});
return t;
});
threadPool.execute(()->{
Object object = null;
System.out.print("result## " + object.toString());
});
答:
答:见上文。
RUNNING
SHUTDOWN
STOP
TIDYING
TERMINATED
答:
/**
创建一个给定初始延迟的间隔性的任务,之后的下次执行时间是上一次任务从执行到结束所需要的时间+* 给定的间隔时间
*/
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
scheduledExecutorService.scheduleWithFixedDelay(()->{
System.out.println("current Time" + System.currentTimeMillis());
System.out.println(Thread.currentThread().getName()+"正在执行");
}, 1, 3, TimeUnit.SECONDS);
/**
创建一个给定初始延迟的间隔性的任务,之后的每次任务执行时间为 初始延迟 + N * delay(间隔)
*/
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
scheduledExecutorService.scheduleAtFixedRate(()->{
System.out.println("current Time" + System.currentTimeMillis());
System.out.println(Thread.currentThread().getName()+"正在执行");
}, 1, 3, TimeUnit.SECONDS);;