在上一篇【线程池】深入理解Executors类时,提到了newSingleThreadExecutor和newFixedThreadPool(1)的区别,查阅了大量资料,自己也做了一些实验,但是还是有很多不清楚的地方,这篇文章主要是用作讨论,如果有大佬有好的回答,拜托请多多指教。
大部分博客中都提到两点:1、Single方法可以保证线程执行顺序,采用FIFO,先提交的任务先执行,而Fixed(1)不保证。
2、在Single方法中,当线程执行出现异常时,它会重新创建一个线程替换之前的线程继续执行,而Fixed(1)不行。
针对上面两点,我做了实验,结果如下:
1、执行顺序
工具类:
public class ThreadPoolUtil implements Runnable{
private Integer index;
public ThreadPoolUtil(Integer index) {
this.index = index;
}
@Override
public void run() {
try {
System.out.println(index+"开始处理线程!");
Thread.sleep(50);
System.out.println("线程标识是:"+this.toString());
//System.out.println(index+"处理结束!");
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
}
创建newSingleThreadExecutor线程池类:
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
System.out.println("---------------newSingleThreadExecutor--------------");
for(int i = 0; i < 200; i++) {
final int index = i;
newSingleThreadExecutor.execute(new ThreadPoolUtil(index));
}
结果:全部按顺序运行。
创建newFixedThreadPool线程池类:
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(1);
System.out.println("------------newFixedThreadPool-------------");
for(int i = 0; i < 1000; i++) {
final int index = i;
newFixedThreadPool.execute(new ThreadPoolUtil(index));
}
结果:也是按顺序执行。
不知道是不是我验证方法有误,1000以内都是按照顺序执行的,和newSingleThreadExecutor没有区别。
2、线程执行出现异常
创建newSingleThreadExecutor线程池类:
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
System.out.println("---------------newSingleThreadExecutor--------------");
for(int i = 0; i < 100; i++) {
final int index = i;
newSingleThreadExecutor.execute(()->{
if(index == 20) {
throw new IllegalStateException("Error");
}
System.out.println(Thread.currentThread() + ".."+index);
});
}
结果:
Thread[pool-1-thread-1,5,main]..19
Exception in thread "pool-1-thread-1" Thread[pool-1-thread-2,5,main]..21
Thread[pool-1-thread-2,5,main]..22
...
...
Thread[pool-1-thread-2,5,main]..32
Thread[pool-1-thread-2,5,main]..33java.lang.IllegalStateException: Error
Thread[pool-1-thread-2,5,main]..34
Thread[pool-1-thread-2,5,main]..35 at threadTest.ThreadPools.lambda$0(ThreadPools.java:57)
Thread[pool-1-thread-2,5,main]..36
Thread[pool-1-thread-2,5,main]..37 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
Thread[pool-1-thread-2,5,main]..38
...
...
Thread[pool-1-thread-2,5,main]..44
Thread[pool-1-thread-2,5,main]..45
at java.lang.Thread.run(Thread.java:748)Thread[pool-1-thread-2,5,main]..46
Thread[pool-1-thread-2,5,main]..47
Thread[pool-1-thread-2,5,main]..48
创建newFixedThreadPool线程池类:
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(1);
System.out.println("------------newFixedThreadPool-------------");
for(int i = 0; i < 100; i++) {
final int index = i;
newFixedThreadPool.execute(()->{
if(index == 20) {
throw new IllegalStateException("Error");
}
System.out.println(Thread.currentThread() + ".."+index);
});
}
结果:
Thread[pool-1-thread-1,5,main]..18
Thread[pool-1-thread-1,5,main]..19
Exception in thread "pool-1-thread-1" Thread[pool-1-thread-2,5,main]..21
Thread[pool-1-thread-2,5,main]..22
Thread[pool-1-thread-2,5,main]..23
...
...
Thread[pool-1-thread-2,5,main]..42
java.lang.IllegalStateException: ErrorThread[pool-1-thread-2,5,main]..43
Thread[pool-1-thread-2,5,main]..44
Thread[pool-1-thread-2,5,main]..45
Thread[pool-1-thread-2,5,main]..46
Thread[pool-1-thread-2,5,main]..47
Thread[pool-1-thread-2,5,main]..48
at threadTest.ThreadPools.lambda$0(ThreadPools.java:27)Thread[pool-1-thread-2,5,main]..49
Thread[pool-1-thread-2,5,main]..50
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)Thread[pool-1-thread-2,5,main]..51
Thread[pool-1-thread-2,5,main]..52
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)Thread[pool-1-thread-2,5,main]..53
Thread[pool-1-thread-2,5,main]..54
at java.lang.Thread.run(Thread.java:748)Thread[pool-1-thread-2,5,main]..55
Thread[pool-1-thread-2,5,main]..56
可以看见,在第20个线程时抛出异常,当前线程的标志改变了,说明新建了线程继续执行。
所以当线程因为异常终止时,newFixedThreadPool(1)可以新建线程继续执行。
做完上面两个实验,我们发现newSingleThreadExecutor和newFixedThreadPool(1)好像没有任何区别,那到底是不是这样呢?我们还是要从源码讲起。
源码分析
newFixedThreadPool源码
/**
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue. At any point, at most
* {@code nThreads} threads will be active processing tasks.
* If additional tasks are submitted when all threads are active,
* they will wait in the queue until a thread is available.
* If any thread terminates due to a failure during execution
* prior to shutdown, a new one will take its place if needed to
* execute subsequent tasks. The threads in the pool will exist
* until it is explicitly {@link ExecutorService#shutdown shutdown}.
*
* @param nThreads the number of threads in the pool
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
其中nThread为线程数量,程序员可自行设置。注释中有句话:如果任何线程在关闭之前的运行期间因为失败停止了,如果需要的话,一个新的线程将代替它继续执行后续的任务。
newSingleThreadExecutor源码
/**
* Creates an Executor that uses a single worker thread operating
* off an unbounded queue. (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* {@code newFixedThreadPool(1)} the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
*
* @return the newly created single-threaded Executor
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
注释中有一段话,大致意思是和其他等效的线程池(比如newFixedThreadPool(1))不同,它保证不能重新配置从而使用其他线程。比如以下代码:
// final ExecutorService single = Executors.newSingleThreadExecutor();
final ExecutorService fixed = Executors.newFixedThreadPool(1);
ThreadPoolExecutor executor = (ThreadPoolExecutor) fixed;
executor.setCorePoolSize(4);
如果新建的线程池类是Single,那么会报错,
Exception in thread "main" java.lang.ClassCastException: java.util.concurrent.Executors$FinalizableDelegatedExecutorService cannot be cast to java.util.concurrent.ThreadPoolExecutor
at threadTest.ThreadPools.main(ThreadPools.java:37)
而Fixed可以正常运行。
源码注释中还有一句话,如果这个单线程在关闭之前的运行期间因为失败停止了,如果需要的话,一个新的线程将代替它继续执行后续的任务。可以看出来这两个方法在意外停止后都会有新的线程继续执行任务,所以文章开头提到了第二点是不成立的。
我们可以发现newSingleThreadExecutor其实就是newFixedThreadPool(1)用FinalizableDelegatedExecutorService()包裹起来的产物。那么我们来看看这个类的源码
static class FinalizableDelegatedExecutorService
extends DelegatedExecutorService {
FinalizableDelegatedExecutorService(ExecutorService executor) {
super(executor);
}
//GC的时候停掉线程池
protected void finalize() {
super.shutdown();
}
}
这个类内部除了构造方法之外只有一个GC时停掉线程池的方法,我们都知道Java中的内存管理一般都是JVM来完成的,程序员不需要做什么事情,所以这个方法我们可以不看。这个类继承了DelegatedExecutorService,我们来看看这个类的源码
/**
* 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);
}
}
这个类有一个注释,大致是说这是一个包装类,只公开ExecutorService接口的ExecutorService方法。你不能自行配置。而这个类也仅仅是调用了传入的AbstractExecutorService类的方法,并没有增加新的方法。
综上所述我们可以得出以下结论:
在小量线程的运行中,newSingleThreadExecutor和newFixedThreadPool(1)都可以保证线程的顺序执行。而从代码上看newSingleThreadExecutor拒绝程序员重新配置加入额外的线程,可以确保线程池中只有一个线程。除此之外,newSingleThreadExecutor和newFixedThreadPool(1)没有任何区别。
写到这里,我有一个疑问:newFixedThreadPool(1)到底能不能保证线程的顺序执行?
如果不能,newSingleThreadExecutor的实现代码几乎和newFixedThreadPool(1)完全一样,那么newSingleThreadExecutor到底是怎么保证线程一定顺序执行的?
如果能,那么是否有必要同时存在如此相似的两个方法?能不能从真正的应用或者其他方面进行解释?
如果有高人指点,感激不尽。
参考资料:https://segmentfault.com/q/1010000011185322