一、前言
JDK1.5中提供了Executor接口,处于java.util.concurrent包下;
创建线程的几种方法
- 创建子类继承Thread类(Thread类实现了Runable接口)并重写run()方法(无返回值),通过子类实例调用start()方法启动;
- 通过实现Runable()接口并重写run()方法(无返回值),start()方法启动;
- 创建子类实现Callable()接口,并重写call()方法,提供了2个额外功能:call()方法可以有int返回值call()方法可以声明抛出异常, 由Callable子类对象创建一个FutureTask对象,由FutureTask对象创建一个Thread对象。
- 通过线程池Executor框架创建
- 通过spring框架提供的ThreadPoolTaskExecutor创建线程池;
二、ThreadPoolExecutor的构造参数和三种常用线程池
下面是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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
- int corePoolSize:线程池维护线程的最小数量.
- int maximumPoolSize:线程池维护线程的最大数量.
- long keepAliveTime:空闲线程的存活时间.
- TimeUnit unit: 时间单位,现有纳秒,微秒,毫秒,秒枚举值.
- BlockingQueue workQueue:任务队列,被提交但尚未被执行的任务
- threadFactory 表示生成线程池中工作线程的线程工厂,用于创建线程一般默认即可
- RejectedExecutionHandler handler: 拒绝策略,用来拒绝一个任务的执行,有两种情况会发生这种情况。
ThreadPoolExecutor的处理流程如下:
- 当池子大小小于corePoolSize就新建线程,并处理请求
- 当池子大小等于corePoolSize,把请求放入workQueue中,池子里的空闲线程就去从workQueue中取任务并处理
- 当workQueue放不下新入的任务时,新建线程入池,并处理请求,如果池子大小撑到了maximumPoolSize就用RejectedExecutionHandler来做拒绝处理
- 另外,当池子的线程数大于corePoolSize的时候,多余的线程会等待keepAliveTime长的时间,如果无请求可处理就自行销毁。其会优先创建 CorePoolSiz 线程, 当继续增加线程时,先放入Queue中,当 CorePoolSiz 和 Queue 都满的时候,就增加创建新线程,当线程达到MaxPoolSize的时候,就会抛出错 误 org.springframework.core.task.TaskRejectedException。另外MaxPoolSize的设定如果比系统支持的线程数还要大时,会抛出java.lang.OutOfMemoryError: unable to create new native thread 异常。
FixedThreadPool
固定线程池的线程数量是固定的,由传入的参数决定。线程 keepAliveTime 为0,即不会因为空闲超时而关闭线程,同时队列是无边界的队列,不会发生任务丢弃。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
CachedThreadPool
单线程池中线程数量固定为1.
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
SingleThreadPoolExcutor
缓存线程池的核心线程corePoolSize 数量为0,但是池中的最大线程数是 无边界。空闲超时为60s,队列用了SynchronousQueue,即任务是立即交付运行的。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
我们参考阿里巴巴的Java开发手册内容:
8. 【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors各个方法的弊端:
1) newFixedThreadPool和newSingleThreadExecutor: 主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2) newCachedThreadPool和newScheduledThreadPool: 主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
9. 【强制】创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。
三、ScheduledThreadPoolExecutor
推荐使用
ScheduledThreadPoolExecutor替代Timer