在Java中,使用线程来异步执行任务,但是线程的创建和销毁都需要一定的开销,如果我们为每一个任务都新开一个线程来执行,必然对计算机造成巨大的压力。 从JDK 5开始,Java可以提供了将工作单元和执行机制分离开的工具,其中工作单元是实现了Runnable或者Callable接口类,而执行机制就是我们下面要讲到的Executor 框架。
Executor 框架的结构
一、任务:即工作单元,实现了Runnable或者Callable接口类;
二、任务的执行:包括了任务执行机制的核心接口Executor,以及从Executor 接口继承的ExecutorService接口,关键类包括(ThreadPoolExecutor和ScheduledThreadPoolExecutor)
三、任务异步执行的结果:对接口的获取,包括接口Future和实现了Future接口的子类FutureTask类
关于任务,由于只要实现了Runnable或者Callable接口类就可以作为一个任务,因此这边就不在叙述了,我们主要着重后面的两点。
任务的执行:
1、ThreadPoolExecutor:通常使用 工厂类Executors或者ExecutorService来创建(当然我们也可以手工创建),Executors或者ExecutorService可以创建
三种类型的ThreadPoolExecutor,分别是newFixedThreadPool、newSingleThreadExecutor、newCachedThreadPool。
1.1、newFixedThreadPool:
ExecutorService executorService2 = Executors.newFixedThreadPool(2);
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
我们可以看到,我们可以指定corePoolSize、maximumPoolSize,并且指定他们的值时一样的,并且空线程的被回收时间为0S,为什么要这样设计呢?原来它使用的是LinkedBlockingQueue默认的大小(即无限大),这时候我们可以任务它是个无界数组,当线程数大于corePoolSize时,后面的线程都被阻塞在了LinkedBlockingQueue队列中,因此其他的几个参数都因此失效了。
1.2、newSingleThreadExecutor:
ExecutorService executorService = Executors.newSingleThreadExecutor();
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
Single,顾名思义:单个的,因此它只有一个线程在执行,参数都与LinkedBlockingQueue差不多,适用于需要保证先进先出按照顺序执行各个任务,并且不会有多个线程是活动的场景。
1.3、newCachedThreadPool:
ExecutorService executorService3 = Executors.newCachedThreadPool();
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
我们分析一下参数:corePoolSize = 0,核心线程数为0,说明有任务提交给executorService3,就会进入阻塞队列,而最后一个参数是SynchronousQueue,此阻塞队列只能
包含阻塞一个任务,由于maximumPoolSize = Integer.MAX_VALUE,因此其他任务都会被新建线程来执行。假如主线程提交任务的速度高于maximumPoolSize 中的线程处理
任务的速度时,就会不断的产生线程,从而耗尽CPU资源和内存资源。另外newCachedThreadPool中线程处理完任务后,线程空闲的时间为60秒,超过这个时间的空闲线程,
将会被回收。
2、ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,由名字中Scheduled(将…列入计划(或时间)表)单词可以看出,这个是一个和时间相关的线程池,主要是用来在给定的延迟之后运行任务,或者定期执行任务;
ExecutorService executorService4 = Executors.newScheduledThreadPool(3);
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
new DelayedWorkQueue());
}
我们先来分析一下参数:corePoolSize 可以进行自定义,说明有任务提交给executorService3,线程数大于corePoolSize时,就会进入阻塞队列,而最后一个参数是DelayedWorkQueue,此阻塞队列是 无界队列,因此参数Integer.MAX_VALUE不起任何作用。
今天有点晚了,明天晚上继续更新。