ThreadPoolExecutor中sumit()和execute方法的区别
- submit 提交的任务可以获取执行结果,而execute 则不能
submit 方法会返回一个 Future ,通过Future.get()方法,可以得到执行结果。 - 执行execute如果有异常会抛出异常,而sumit则是把异常存储起来,直到调用future.get()方法返回异常调用堆栈。
ThreadPoolExecutor的submit有父类AbstractExecutorService所实现,内部实际还是调用ThreadPoolExecutor的execute方法,只不过参数替换为了FutureTask,可以用来接收线程执行的结果。
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
ThreadPoolExecutor机制
参考ThreadPoolExecutor机制
ThreadPoolExecutor作为java.util.concurrent包对外提供线程池的实现,通过内部线程池的形式对外提供管理任务执行,线程调度,线程池管理等服务。可配置性较高,可以通过参数设置来实现不同的线程池机制。
核心构造器方法
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;
}
参数说明:
线程管理的规则:
ThreadPoolExecutor中有corePoolSize,maximumPoolSize,workQueue,以及rejectedHandler的概念:
1.当前线程池中的线程数目小于corePoolSize时,线程池将创建一个新线程用来执行新提交的任务。即使此时线程池中存在空闲线程。这点需要验证??为何不利用空闲线程
2.当线程池中线程数据达到corePoolSize,新提交的任务将被放入workQueue中,等待线程池中线程调度执行.注意任务队列有有限队列和无限队列的区别。
3.当workQueue队列满(有界队列),且maximumPoolSize>corePoolSize,此时线程池会创建新线程执行新提交的任务。
4.当提交的任务总数>maximumPoolSize,新提交的任务交给rejectedHandler来处理。这里可以设置自定义阻塞线程池来进行任务的执行。
5.当线程池中线程数据超过corePoolSize,空闲的非核心线程空闲时间达到keepAliveTime,则关闭该线程。
6.设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭
排队策略:
排队有三种通用策略:
1.直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集合时出现锁定。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
2.无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙的情况下将新任务加入队列。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
3.有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。
常用线程池配置方案
Executors工具类提供4种常用的线程池方案
1.构造一个固定线程数目的线程池,配置的corePoolSize与maximumPoolSize大小相同,同时使用了一个无界LinkedBlockingQueue存放阻塞任务,因此多余的任务将存在再阻塞队列,不会由RejectedExecutionHandler处理 .当要处理的任务量巨大时,就会有OOM的风险。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
2.构造一个缓冲功能的线程池,配置corePoolSize=0,maximumPoolSize=Integer.MAX_VALUE,keepAliveTime=60s,以及一个无容量的阻塞队列 SynchronousQueue,因此任务提交之后,将会创建新的线程执行;线程空闲超过60s将会销毁。即,来了新任务直接创建线程执行。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
3.构造一个只支持一个线程的线程池,配置corePoolSize=maximumPoolSize=1,无界阻塞队列LinkedBlockingQueue;保证任务由一个线程串行执行。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
4.构造有定时功能的线程池,配置corePoolSize,无界延迟阻塞队列DelayedWorkQueue;有意思的是:maximumPoolSize=Integer.MAX_VALUE,由于DelayedWorkQueue是无界队列,所以这个值是没有意义的 。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
注意这里keepAliveTime 默认为0,代表不会因为空闲超时而销毁该线程。同时如果allowsCoreThreadTimeOut属性为true情况下,keepAliveTime会报异常。
public void setKeepAliveTime(long time, TimeUnit unit) {
if (time < 0)
throw new IllegalArgumentException();
if (time == 0 && allowsCoreThreadTimeOut())
throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
long keepAliveTime = unit.toNanos(time);
long delta = keepAliveTime - this.keepAliveTime;
this.keepAliveTime = keepAliveTime;
if (delta < 0)
interruptIdleWorkers();
}
自定义非阻塞线程池
package org.ldy.Demo;
import org.junit.Test;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by Administrator on 2017/6/6.
*/
public class ThreadPoolExecutorDemo {
@Test
public void testThreadPoolExecutor() {
//ExecutorService pool = CustomThreadPoolExecutor.getCustomThreadPoolExecutor();
ThreadPoolExecutor pool = CustomThreadPoolExecutor.getCustomThreadPoolExecutor();
for (int i = 1; i <= 50; i++) {
System.out.println("提交第" + i +"个任务!");
pool.execute(new Runnable() {
// TODO: 2017/6/6 如何在父线程中给子任务传递变量值
//ThreadLocal taskNum = new ThreadLocal();
public void run() {
//这里模拟正在执行任务
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("The task is running ,currentThread is " + Thread.currentThread().getName());
}
});
}
// TODO: 2017/6/6 如何确定线程池中的所有线程都执行完呢
pool.shutdown();
System.out.println("shutdown is done" + pool.getCompletedTaskCount());
while (true) {
if (pool.isTerminated()) {
System.out.println("pool is terminated" + pool.getCompletedTaskCount() + " " + pool.getActiveCount());
break;
}
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/*try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
}
/**
* 自定义非阻塞线程池,核心线程数为10,最大线程数为20,缓冲队列大小为10,
* 提交任务
*/
class CustomThreadPoolExecutor {
private static ThreadPoolExecutor pool = null;
private CustomThreadPoolExecutor() {
}
public static ThreadPoolExecutor getCustomThreadPoolExecutor() {
// TODO: 2017/6/6 这里考虑实现单例模式,一个jvm或者是一个微服务中只有线程池,同时还需要加入线程组的概念
if (pool == null) {
init();
}
return pool;
}
private static void init() {
pool = new ThreadPoolExecutor(10, 20, 30, TimeUnit.MINUTES,
new ArrayBlockingQueue<Runnable>(10),
new CustomThreadFactory(),
new CustomRejectedExecutionHandler());
}
private static class CustomRejectedExecutionHandler implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("TaskCount is " + executor.getTaskCount()
+ " " + executor.getActiveCount()
+ " " + executor.getCompletedTaskCount()
+ " " + executor.getCorePoolSize()
+ " " + executor.getKeepAliveTime(TimeUnit.MINUTES)
+ " " + executor.getLargestPoolSize()
+ " " + executor.getPoolSize());
System.out.println("I'm in rejectedHandler,record the error log");
}
}
private static class CustomThreadFactory implements ThreadFactory {
//定义原子变量,用来保证变量操作的原子性。
private AtomicInteger count = new AtomicInteger(0);
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
String tName = CustomThreadPoolExecutor.class.getSimpleName() + count.addAndGet(1);
t.setName(tName);
//为了证明,当线程数小于corePoolSize时候,提交任务是否立即创建一个新线程
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm in ThreadFactory,create Thread is " + tName);
return t;
}
}
public static void destroy() {
if (pool != null) {
pool.shutdown();
}
}
}
如下为执行结果:
提交第1个任务!
I’m in ThreadFactory,create Thread is CustomThreadPoolExecutor1
提交第2个任务!
The task is running ,currentThread is CustomThreadPoolExecutor1
I’m in ThreadFactory,create Thread is CustomThreadPoolExecutor2
提交第3个任务!
The task is running ,currentThread is CustomThreadPoolExecutor2
I’m in ThreadFactory,create Thread is CustomThreadPoolExecutor3
提交第4个任务!
The task is running ,currentThread is CustomThreadPoolExecutor3
I’m in ThreadFactory,create Thread is CustomThreadPoolExecutor4
提交第5个任务!
The task is running ,currentThread is CustomThreadPoolExecutor4
I’m in ThreadFactory,create Thread is CustomThreadPoolExecutor5
提交第6个任务!
The task is running ,currentThread is CustomThreadPoolExecutor5
I’m in ThreadFactory,create Thread is CustomThreadPoolExecutor6
提交第7个任务!
The task is running ,currentThread is CustomThreadPoolExecutor6
I’m in ThreadFactory,create Thread is CustomThreadPoolExecutor7
提交第8个任务!
The task is running ,currentThread is CustomThreadPoolExecutor7
I’m in ThreadFactory,create Thread is CustomThreadPoolExecutor8
提交第9个任务!
The task is running ,currentThread is CustomThreadPoolExecutor8
I’m in ThreadFactory,create Thread is CustomThreadPoolExecutor9
提交第10个任务!
The task is running ,currentThread is CustomThreadPoolExecutor9
I’m in ThreadFactory,create Thread is CustomThreadPoolExecutor10
提交第11个任务!
提交第12个任务!
提交第13个任务!
提交第14个任务!
提交第15个任务!
提交第16个任务!
提交第17个任务!
提交第18个任务!
提交第19个任务!
提交第20个任务!
提交第21个任务!
提交第22个任务!
提交第23个任务!
提交第24个任务!
提交第25个任务!
提交第26个任务!
提交第27个任务!
提交第28个任务!
提交第29个任务!
提交第30个任务!
The task is running ,currentThread is CustomThreadPoolExecutor2
The task is running ,currentThread is CustomThreadPoolExecutor1
The task is running ,currentThread is CustomThreadPoolExecutor10
The task is running ,currentThread is CustomThreadPoolExecutor7
The task is running ,currentThread is CustomThreadPoolExecutor6
The task is running ,currentThread is CustomThreadPoolExecutor4
The task is running ,currentThread is CustomThreadPoolExecutor5
The task is running ,currentThread is CustomThreadPoolExecutor3
The task is running ,currentThread is CustomThreadPoolExecutor9
The task is running ,currentThread is CustomThreadPoolExecutor8
The task is running ,currentThread is CustomThreadPoolExecutor1
The task is running ,currentThread is CustomThreadPoolExecutor2
The task is running ,currentThread is CustomThreadPoolExecutor3
The task is running ,currentThread is CustomThreadPoolExecutor9
The task is running ,currentThread is CustomThreadPoolExecutor8
The task is running ,currentThread is CustomThreadPoolExecutor7
The task is running ,currentThread is CustomThreadPoolExecutor10
The task is running ,currentThread is CustomThreadPoolExecutor5
The task is running ,currentThread is CustomThreadPoolExecutor4
The task is running ,currentThread is CustomThreadPoolExecutor6
I’m in ThreadFactory,create Thread is CustomThreadPoolExecutor11
提交第31个任务!
提交第32个任务!
提交第33个任务!
提交第34个任务!
提交第35个任务!
提交第36个任务!
提交第37个任务!
提交第38个任务!
提交第39个任务!
提交第40个任务!
提交第41个任务!
提交第42个任务!
提交第43个任务!
提交第44个任务!
提交第45个任务!
提交第46个任务!
提交第47个任务!
提交第48个任务!
提交第49个任务!
提交第50个任务!
shutdown is done29
The task is running ,currentThread is CustomThreadPoolExecutor3
The task is running ,currentThread is CustomThreadPoolExecutor2
The task is running ,currentThread is CustomThreadPoolExecutor5
The task is running ,currentThread is CustomThreadPoolExecutor10
The task is running ,currentThread is CustomThreadPoolExecutor7
The task is running ,currentThread is CustomThreadPoolExecutor8
The task is running ,currentThread is CustomThreadPoolExecutor11
The task is running ,currentThread is CustomThreadPoolExecutor9
The task is running ,currentThread is CustomThreadPoolExecutor1
The task is running ,currentThread is CustomThreadPoolExecutor6
The task is running ,currentThread is CustomThreadPoolExecutor4
The task is running ,currentThread is CustomThreadPoolExecutor10
The task is running ,currentThread is CustomThreadPoolExecutor2
The task is running ,currentThread is CustomThreadPoolExecutor5
The task is running ,currentThread is CustomThreadPoolExecutor9
The task is running ,currentThread is CustomThreadPoolExecutor1
The task is running ,currentThread is CustomThreadPoolExecutor11
The task is running ,currentThread is CustomThreadPoolExecutor8
The task is running ,currentThread is CustomThreadPoolExecutor7
The task is running ,currentThread is CustomThreadPoolExecutor3
The task is running ,currentThread is CustomThreadPoolExecutor6
pool is terminated50 0
Process finished with exit code 0
背景:
corePoolSize 10
maximumPoolSize 20
arrayQueue 10
结论:
1.因为线程池中线程数据超过maximumPoolSize(20)时候,提交任务到rejectedhandler处理,这里采用记录异常的方式来处理,相应的任务也就丢弃了。
2.极端情况下,当提交的任务在执行时都睡眠一个较长的时间,那么提交任务超过corePoolSize+arrayQueue.size,但是还没超过maximumPoolSize+arrayQueue.size时,也是会创建线程来执行任务。当提交任务超过maximumPoolSize+arrayQueue.size时,才会进入rejectedHandler。即maximumPoolSize的大小是不包含缓冲队列的长度的。
2.当线程数小于corePoolSize时,每提交一个任务都会立即创建一个线程与之对应(即提交任务的线程,和创建执行任务的线程为同一个线程)。通过分析源码,并没有发现这种对应关系,有待验证。
自定义阻塞线程池
execute源码如下:这里采用workQueue.offer(command)的方式来加入缓冲队列,为非阻塞方法。要想实现阻塞式的线程池,是需要在rejectedExecution函数中采用阻塞式的方式把提交的任务插入到缓冲队列中。代码如下
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
private static class CustomRejectedExecutionHandler implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
try {
executor.getQueue().put(r);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
当提交任务被拒绝时,进入拒绝机制,我们实现拒绝方法,把任务重新用阻塞提交方法put提交,实现阻塞提交任务功能,防止队列过大,OOM。
总结:
1、用ThreadPoolExecutor自定义线程池,看线程的用途,如果任务量不大,可以用无界队列,如果任务量非常大,要用有界队列,防止OOM
2、如果任务量很大,还要求每个任务都处理成功,要对提交的任务进行阻塞提交,重写拒绝机制,改为阻塞提交。保证不抛弃一个任务
3、最大线程数一般设为2N+1最好,N是CPU核数
4、核心线程数,看应用,如果是任务,一天跑一次,设置为0,合适,因为跑完就停掉了,如果是常用线程池,看任务量,是保留一个核心还是几个核心线程数
5、如果要获取任务执行结果,用CompletionService,但是注意,获取任务的结果的要重新开一个线程获取,如果在主线程获取,就要等任务都提交后才获取,就会阻塞大量任务结果,队列过大OOM,所以最好异步开个线程获取结果
实现调度功能的executor
class SerialExecutor implements Executor {
final Queue<Runnable> tasks = new ArrayDeque<Runnable>();
final Executor executor;
Runnable active;
SerialExecutor(Executor executor) {
this.executor = executor;
}
public synchronized void execute(final Runnable r) {
tasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (active == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((active = tasks.poll()) != null) {
executor.execute(active);
}
}
}