一、Executor框架
先看Executor框架下的结构图
1、ThreadPoolExecutor
ThreadPoolExecutor是Executor框架中最核心的类,它是线程的实现类,通过Executor框架的工具类Executors,可以创建3种类型的ThreadPoolExecutor,二中会详细说明。
2、ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor继承自ThreadPoolExecutor。它主要用来在给定的延迟之后运行任务,或者定期执行任务。ScheduledThreadPoolExecutor的功能与Timer类似,但ScheduledThreadPoolExecutor功能更强大、更灵活。Timer对应的是单个后台线程,ScheduledThreadPoolExecutor可以在构造函数中指定多个对应的后台线程数。Executors的newScheduledThreadPool(corePoolSize)就是对其的一种实现。
3、FutureTask
FutureTask实现了RunnableFuture接口,而RunnableFuture接口继承了Runnable, Future接口。因此,FutureTask可以交给Executor执行,也可以由调用线程直接执行(FutureTask.run())。
二、Executors的四个静态方法
java.util.concurrent.Executors类有四个静态方法
1、Executors.newSingleThreadExecutor()
同时只有一个线程来执行任务,适用于有顺序的任务的应用场景,能保证执行顺序,先提交的先执行
当线程执行中出现异常,去创建一个新的线程替换之,处理失败线程任务,外层包了个FinalizableDelegatedExecutorService只暴露ExecutorService方法,避免ThreadPoolExecutor其他方法对外暴露,
可以看到corePoolSize与maximumPoolSize都是1,用无界队列LinkedBlockingQueue做工作队列,提交多个任务按FIFO排队,可接受无数个线程排队,直到内存溢出。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
2、Executors.newFixedThreadPool(nThreads)
创建固定长度的线程池,同时只能有nThreads个线程执行任务,线程数量大于1时并不保证顺序执行,可以看到corePoolSize与maximumPoolSize都是nThreads,即核心线程数与最大线程数相等,同样用无界队列LinkedBlockingQueue做工作队列,可接受无数个线程排队,直到内存溢出。
核心线程与最大线程数相等,一旦初始化,不会频繁创建销毁线程,适合CPU密集型(系统的硬盘、内存性能相对CPU要好很多)机器
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
3、Executors.newCachedThreadPool()
会创建一个缓存区,将初始化的线程缓存起来,会终止并且从缓存中移除已有60秒未被使用的线程。
新任务到时,如果线程有可用的,就使用之前创建好的线程,如果线程没有可用的,就新创建线程,
可以看到corePoolSize等0,maximumPoolSize是Integer.MAX_VALUE,即无限大,适用于耗时较短的任务场景
用同步队列SynchronousQueue作为工作队列,一个不存元素,没有缓冲的的队列,每个插入操作必须等到另一个线程调用移除操作,即生产线程对其的插入操作put必须等待消费线程的移除操作take,否则插入操作一直处于阻塞状态。
SynchronousQueue充当一个传递者的角色。好比制造业的流水线,B要想接收A流过来的产品,必须等到B把手中的产品传递给C,否则B不能接收A流过来的产品。产品从A经过B时候,基本不停留,直接到C。
不初始核心线程,按需创建,并缓存60秒,可能会频繁创建于销毁线程,适合IO密集型(系统的CPU性能相对硬盘、内存要好很多)机器
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
4、Executors.newScheduledThreadPool(corePoolSize)
周期性执行任务的线程池,按照某种特定的计划执行线程中的任务,核心线程数即为传参数corePoolSize,最大线程数量为Integer.MAX_VALUE,用延时队列DelayedWorkQueue作为工作队列。DelayedWorkQueue队列是个优先级队列,队列中每个元素都有个过期时间,当从队列获取元素时候,只有过期元素才会出队列,即延时时间越短地就排在队列的前面,先被获取执行
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
//new ScheduledThreadPoolExecutor(corePoolSize)
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
//super
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
ScheduledExecutorService有几个重要的方法,如下所示
(1)schedule(command, delay, unit)
任务延迟多长时间执行
public class Task implements Runnable {
@Override
public void run() {
int time = (int) (System.currentTimeMillis()/1000);
System.out.println(Thread.currentThread().getName()+"--"+time+"秒");
}
public static void main(String[] args) {
int time = (int) (System.currentTimeMillis()/1000);
System.out.println(Thread.currentThread().getName()+"--"+time+"秒");
System.out.println("==========开始执行=============");
ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(2);
for (int i = 0; i < 2; i++) {
scheduled.schedule(new Task(), 10, TimeUnit.SECONDS);//延迟10秒执行
}
}
}
输出如下,可以看到 有延迟了10秒执行
main--1542124548秒
==========开始执行=============
pool-1-thread-2--1542124558秒
pool-1-thread-1--1542124558秒
(2)scheduleAtFixedRate(command, initialDelay, period, unit)
按固定频率执行,只要时间间隔到,不管线程池上批次线程是否结束,都开始下一批次次线程执行
public class Task implements Runnable {
@Override
public void run() {
int time=(int) (System.currentTimeMillis()/1000);
try {
Thread.sleep(3000);//睡眠3秒
System.out.println(time+"秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(2);
scheduled.scheduleAtFixedRate(new Task(), 10, 5, TimeUnit.SECONDS);
}
}
输出如下,可以看到每隔5秒执行一次,睡眠3秒丝毫不影响间隔频率
main--1542124880秒
==========开始执行=============
pool-1-thread-1--1542124890秒
pool-1-thread-2--1542124890秒
pool-1-thread-2--1542124895秒
pool-1-thread-1--1542124895秒
pool-1-thread-2--1542124900秒
pool-1-thread-1--1542124900秒
pool-1-thread-1--1542124905秒
pool-1-thread-2--1542124905秒
(3)scheduleWithFixedDelay(command, initialDelay, period, unit)
按固定间隔,间隔可以延迟执行,只要线程池里上批次是没有结束,不管时间间隔是否达到,都不开始下批次执行,直到上批次线程结束执行
public class Task implements Runnable {
@Override
public void run() {
int time=(int) (System.currentTimeMillis()/1000);
try {
Thread.sleep(3000);//睡眠3秒
System.out.println(time+"秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(2);
scheduled .scheduleWithFixedDelay(new Task(), 10, 5, TimeUnit.SECONDS);
}
}
输出如下,间隔5秒,加上睡眠3秒,可以看到每隔8秒执行一次
main--1542125287秒
==========开始执行=============
pool-1-thread-2--1542125297秒
pool-1-thread-1--1542125297秒
pool-1-thread-2--1542125305秒
pool-1-thread-1--1542125305秒
pool-1-thread-1--1542125313秒
pool-1-thread-2--1542125313秒
pool-1-thread-2--1542125321秒
pool-1-thread-1--1542125321秒
(4)schedule(Callable<V> callable, long delay, TimeUnit unit)
延迟多长时间执行,get返回ScheduledFuture<V>,get时候会阻塞当前线程运行
public class Task implements Callable<String>{
private int i;
public Task(int i) {
this.i=i;
}
@Override
public String call() throws Exception {
return Thread.currentThread().getName()+"---"+i;
}
public static void main(String[] args) throws Exception {//为了清爽,直接抛Exception
ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(3);
System.out.println(System.currentTimeMillis()/1000+"秒,开始");
ScheduledFuture<String> future = null;
for (int i = 0; i < 5; i++) {
future=scheduled.schedule(new Task(i),5,TimeUnit.SECONDS);//延迟5秒执行
//future.cancel(true);//取消任务
String result = future.get();
//String result = future.get(6, TimeUnit.SECONDS);//超时抛出异常
System.out.println(System.currentTimeMillis()/1000+"秒---"+result);
System.out.println("---被get阻塞---"+i);
}
}
}
输出如下, 线程延迟5秒执行,future.get()可以得到返回结果,get(6, TimeUnit.SECONDS)超时不返回抛出异常
只有get返回结果时候,才会继续下一步,也就是说当前的 线程被阻塞了
1542164733秒,开始
1542164738秒---pool-1-thread-1---0
---被get阻塞---0
1542164743秒---pool-1-thread-1---1
---被get阻塞---1
1542164748秒---pool-1-thread-2---2
---被get阻塞---2
1542164753秒---pool-1-thread-1---3
---被get阻塞---3
1542164758秒---pool-1-thread-3---4
---被get阻塞---4
三、SingleThreadExeutor对比FixedThreadExeutor(1)
Executors.newSingleThreadExeutor()和Executors.newFixedThreadPool(1)
二者并没有实质区别,两者都能保证线程按顺序执行,遇到异常都会创建新线程代替异常线程继续执行任务
1、在执行顺序上
Executors.newSingleThreadExecutor(),保持有序执行
ExecutorService executor = Executors.newSingleThreadExecutor();
for(int i = 0; i < 50; i++) {
final int j = i;
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread() + "--"+j);
}
});
}
Thread[pool-1-thread-1,5,main]--0
Thread[pool-1-thread-1,5,main]--1
Thread[pool-1-thread-1,5,main]--2
Thread[pool-1-thread-1,5,main]--3
Thread[pool-1-thread-1,5,main]--4
Thread[pool-1-thread-1,5,main]--5
Thread[pool-1-thread-1,5,main]--6
Thread[pool-1-thread-1,5,main]--7
Thread[pool-1-thread-1,5,main]--8
Thread[pool-1-thread-1,5,main]--9
..................
Thread[pool-1-thread-1,5,main]--43
Thread[pool-1-thread-1,5,main]--44
Thread[pool-1-thread-1,5,main]--45
Thread[pool-1-thread-1,5,main]--46
Thread[pool-1-thread-1,5,main]--47
Thread[pool-1-thread-1,5,main]--48
Thread[pool-1-thread-1,5,main]--49
Executors.newFixedThreadPool(1),经过多次试验,同样保持有序执行
ExecutorService executor = Executors.newFixedThreadPool(1);
for(int i = 0; i < 50; i++) {
final int j = i;
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread() + "--"+j);
}
});
}
Thread[pool-1-thread-1,5,main]--0
Thread[pool-1-thread-1,5,main]--1
Thread[pool-1-thread-1,5,main]--2
Thread[pool-1-thread-1,5,main]--3
Thread[pool-1-thread-1,5,main]--4
Thread[pool-1-thread-1,5,main]--5
Thread[pool-1-thread-1,5,main]--6
Thread[pool-1-thread-1,5,main]--7
Thread[pool-1-thread-1,5,main]--8
Thread[pool-1-thread-1,5,main]--9
..................
Thread[pool-1-thread-1,5,main]--43
Thread[pool-1-thread-1,5,main]--44
Thread[pool-1-thread-1,5,main]--45
Thread[pool-1-thread-1,5,main]--46
Thread[pool-1-thread-1,5,main]--47
Thread[pool-1-thread-1,5,main]--48
Thread[pool-1-thread-1,5,main]--49
由于同时只有一个线程,都用LinkedBlockingQueue作为阻塞队列,先进先出,保证了线程的执行顺序
2、在处理异常上
Executors.newSingleThreadExecutor(),可以看到在j=25的时候,by zero异常后,线程名由Thread[pool-1-thread-1,5,main]变为Thread[pool-1-thread-2,5,main],Thread[pool-1-thread-2,5,main]就是新创建的线程,替代了Thread[pool-1-thread-1,5,main]继续执行后边任务
ExecutorService executor = Executors.newSingleThreadExecutor();
for(int i = 0; i < 50; i++) {
final int j = i;
executor.execute(new Runnable() {
@Override
public void run() {
if (j==25) {
int num = 1/0;//除零错误,抛出异常
}
System.out.println(Thread.currentThread() + "--"+j);
}
});
}
Thread[pool-1-thread-1,5,main]--0
Thread[pool-1-thread-1,5,main]--1
Thread[pool-1-thread-1,5,main]--2
Thread[pool-1-thread-1,5,main]--3
Thread[pool-1-thread-1,5,main]--4
Thread[pool-1-thread-1,5,main]--5
........................
Thread[pool-1-thread-1,5,main]--23
Thread[pool-1-thread-1,5,main]--24
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
Thread[pool-1-thread-2,5,main]--26
Thread[pool-1-thread-2,5,main]--27
Thread[pool-1-thread-2,5,main]--28
Thread[pool-1-thread-2,5,main]--29
Thread[pool-1-thread-2,5,main]--30
at com.ultradata.paintel.common.test.TestThreadPool$1.run(TestThreadPool.java:17)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)Thread[pool-1-thread-2,5,main]--31
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Thread[pool-1-thread-2,5,main]--32
Thread[pool-1-thread-2,5,main]--33
............................
Thread[pool-1-thread-2,5,main]--47
Thread[pool-1-thread-2,5,main]--48
Thread[pool-1-thread-2,5,main]--49
Executors.newFixedThreadPool(1),同样,在j=25的时候,by zero异常后,线程名由Thread[pool-1-thread-1,5,main]变为Thread[pool-1-thread-2,5,main],Thread[pool-1-thread-2,5,main]就是新创建的线程,替代了Thread[pool-1-thread-1,5,main]继续执行后边任务
ExecutorService executor = Executors.newFixedThreadPool(1);
for(int i = 0; i < 50; i++) {
final int j = i;
executor.execute(new Runnable() {
@Override
public void run() {
if (j==25) {
int num = 1/0;//除零错误,抛出异常
}
System.out.println(Thread.currentThread() + "--"+j);
}
});
}
Thread[pool-1-thread-1,5,main]--0
Thread[pool-1-thread-1,5,main]--1
Thread[pool-1-thread-1,5,main]--2
.......................
Thread[pool-1-thread-1,5,main]--23
Thread[pool-1-thread-1,5,main]--24
Exception in thread "pool-1-thread-1" Thread[pool-1-thread-2,5,main]--26
Thread[pool-1-thread-2,5,main]--27
Thread[pool-1-thread-2,5,main]--28
Thread[pool-1-thread-2,5,main]--29
Thread[pool-1-thread-2,5,main]--30
java.lang.ArithmeticException: / by zeroThread[pool-1-thread-2,5,main]--31
Thread[pool-1-thread-2,5,main]--32 at com.ultradata.paintel.common.test.TestThreadPool$1.run(TestThreadPool.java:17)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Thread[pool-1-thread-2,5,main]--33
Thread[pool-1-thread-2,5,main]--34
...............................
Thread[pool-1-thread-2,5,main]--48
Thread[pool-1-thread-2,5,main]--49
可以看出,二者是在功能方面是没有任何区别的。
3、在源码实现上
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
可以看到newSingleThreadExecutor外层多了个FinalizableDelegatedExecutorService,进去看代码
static class FinalizableDelegatedExecutorService extends DelegatedExecutorService {
FinalizableDelegatedExecutorService(ExecutorService executor) {
super(executor);
}
protected void finalize() {
super.shutdown();
}
}
可以看到FinalizableDelegatedExecutorService继承了DelegatedExecutorService,super(executor)调用了父类的构造方法
/**
* A wrapper class that exposes only the ExecutorService methods
*of an ExecutorService implementation.
*/
static class DelegatedExecutorService extends AbstractExecutorService {
private final ExecutorService e;
DelegatedExecutorService(ExecutorService executor) { e = executor; }
public void execute(Runnable command) { e.execute(command); }
public void shutdown() { e.shutdown(); }
public List<Runnable> shutdownNow() { return e.shutdownNow(); }
public boolean isShutdown() { return e.isShutdown(); }
public boolean isTerminated() { return e.isTerminated(); }
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
return e.awaitTermination(timeout, unit);
}
public Future<?> submit(Runnable task) {
return e.submit(task);
}
public <T> Future<T> submit(Callable<T> task) {
return e.submit(task);
}
public <T> Future<T> submit(Runnable task, T result) {
return e.submit(task, result);
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException {
return e.invokeAll(tasks);
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException {
return e.invokeAll(tasks, timeout, unit);
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException {
return e.invokeAny(tasks);
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return e.invokeAny(tasks, timeout, unit);
}
}
private final ExecutorService e;
DelegatedExecutorService(ExecutorService executor) { e = executor; }
可以看到把当前的线程executor赋给ExecutorService对象e,并没有什么其它操作。从注释也可知: DelegatedExecutorService是ExcutoService的一个实现类,只暴露ExcutoService的方法,避免了ThreadPoolExecutor对外方法的全部暴露,从而避免外部操作重新配置线程池,保证了始终只有一个线程。
总上所述,二者并没有什么实质区别,因为1个线程,LinkedBlockingQueue作为队列,都能保证任务有序执行
遇到异常,都会创建新线程替代当前线程继续执行后边的任。