Executors四中线程类型对比

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lzxlfly/article/details/83964542

一、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做工作队列,可接受无数个线程排队,直到内存溢出。

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。

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作为队列,都能保证任务有序执行

遇到异常,都会创建新线程替代当前线程继续执行后边的任。

猜你喜欢

转载自blog.csdn.net/lzxlfly/article/details/83964542