ExecutorService接口是Java内置的线程池接口,整体的继承关系如下:
其常用方法有:
- void shutdown() - 启动一次顺序关闭,执行以前提交的任务,但不接受新任务
- List
shutdownNow() - 停止所有正在执行的任务,暂停处理正在等待的任务,并返回等待执行的任务列表 Future submit(Callable task) - 执行带返回值的任务,返回一个Future对象 - Future> submit(Runnable task) - 执行 Runnable 任务,并返回一个表示该任务的 Future
Future submit(Runnable task, T result) - 执行 Runnable 任务,并返回一个表示该任务的 Future
通过Executors类中的静态方法来获取ExecutorService
1.static ExecutorService newCachedThreadPool()
创建一个默认的线程池对象,里面的线程可重用,且在第一次使用时才创建
2.static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)
线程池中的所有线程都使用ThreadFactory来创建,这样的线程无需手动启动,自动执行;
3.static ExecutorService newFixedThreadPool(int nThreads)
创建一个可重用固定线程数的线程池
4.static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
创建一个可重用固定线程数的线程池且线程池中的所有线程都使用ThreadFactory来创建。
5.static ExecutorService newSingleThreadExecutor()
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
6.static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)
创建一个使用单个 worker 线程的 Executor,且线程池中的所有线程都使用ThreadFactory来创建
static ExecutorService newCachedThreadPool()方法定义如下:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
- 它是一个可以无限扩大的线程池;
- 它比较适合处理执行时间比较小的任务;
- corePoolSize为0,maximumPoolSize为无限大,意味着线程数量可以无限大;
- keepAliveTime为60S,意味着线程空闲时间超过60S就会被杀死;
- 采用SynchronousQueue装等待的任务,这个阻塞队列没有存储空间,这意味着只要有请求到来,就必须要找到一条工作线程处理他,如果当前没有空闲的线程,那么就会再创建一条新的线程。
如下的例子:
public class MyTest01 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
//提交任务
for (int i = 1; i <= 10; i++) {
executorService.submit(new MyRunnable(i));
}
}
}
class MyRunnable implements Runnable {
private int id;
public MyRunnable(int id) {
this.id = id;
}
@Override
public void run() {
//获取线程的名称,并打印
String name = Thread.currentThread().getName();
System.out.println(name + "执行了任务..." + id);
}
}
输出结果为:
pool-1-thread-8执行了任务...8 pool-1-thread-9执行了任务...9 pool-1-thread-1执行了任务...1 pool-1-thread-7执行了任务...7 pool-1-thread-3执行了任务...3 pool-1-thread-6执行了任务...6 pool-1-thread-5执行了任务...5 pool-1-thread-4执行了任务...4 pool-1-thread-2执行了任务...2 pool-1-thread-10执行了任务...10newFixedThreadPool
static ExecutorService newFixedThreadPool(int nThreads) 方法定义如下:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new linkedBlockingQueue());
}
- 它是一种固定大小的线程池;
- corePoolSize和maximunPoolSize都为用户设定的线程数量nThreads;
- keepAliveTime为0,意味着一旦有多余的空闲线程,就会被立即停止掉;但这里keepAliveTime无效;
- 阻塞队列采用了linkedBlockingQueue,它是一个无界队列;由于阻塞队列是一个无界队列,因此永远不可能拒绝任务;
- 由于采用了无界队列,实际线程数量将永远维持在nThreads,因此maximumPoolSize和keepAliveTime将无效。
如下的例子,线程个数为3:
ExecutorService executorService = Executors.newFixedThreadPool(3, new ThreadFactory() {
int n = 1;
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "自定义的线程名称" + (n++));
}
});
//提交任务
for (int i = 1; i <= 10; i++) {
executorService.submit(new MyRunnable(i));
}
输出结果如下:
自定义的线程名称3执行了任务...3 自定义的线程名称2执行了任务...2 自定义的线程名称1执行了任务...1 自定义的线程名称3执行了任务...4 自定义的线程名称3执行了任务...5 自定义的线程名称2执行了任务...6 自定义的线程名称3执行了任务...7 自定义的线程名称3执行了任务...9 自定义的线程名称2执行了任务...8 自定义的线程名称1执行了任务...10newSingleThreadExecutor
static ExecutorService newSingleThreadExecutor()方法定义如下:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new linkedBlockingQueue()));
}
- 它只会创建一条工作线程处理任务;
- 采用的阻塞队列为linkedBlockingQueue;
如下的例子:
ExecutorService executorService = Executors.newSingleThreadExecutor();
//提交任务
for (int i = 1; i <= 10; i++) {
executorService.submit(new MyRunnable(i));
}
输出结果如下:
pool-1-thread-1执行了任务...1 pool-1-thread-1执行了任务...2 pool-1-thread-1执行了任务...3 pool-1-thread-1执行了任务...4 pool-1-thread-1执行了任务...5 pool-1-thread-1执行了任务...6 pool-1-thread-1执行了任务...7 pool-1-thread-1执行了任务...8 pool-1-thread-1执行了任务...9 pool-1-thread-1执行了任务...10
可见只有一个线程
shutdown() vs shutdownNow()shutdown()方法,仅仅是不再接受新的任务,以前的任务还会继续执行
如下例子:
ExecutorService executorService = Executors.newSingleThreadExecutor();
//提交任务
for (int i = 1; i <= 10; i++) {
executorService.submit(new MRunnable(i));
}
//关闭线程
executorService.shutdown();
executorService.submit(new MyRunnable(888));
此时输出结果为:
pool-1-thread-1执行了任务...1 pool-1-thread-1执行了任务...2 pool-1-thread-1执行了任务...3 pool-1-thread-1执行了任务...4 pool-1-thread-1执行了任务...5 pool-1-thread-1执行了任务...6 pool-1-thread-1执行了任务...7 pool-1-thread-1执行了任务...8 pool-1-thread-1执行了任务...9 pool-1-thread-1执行了任务...10
可见,关闭后new MyRunnable(888)并没有执行,但其它已加入的任务还是会继续执行
shutdownNow()立刻关闭线程池,如果线程池中还有缓存的任务没有执行,则取消执行,并返回这些没有执行的任务
如下的例子:
ExecutorService executorService = Executors.newSingleThreadExecutor();
//提交任务
for (int i = 1; i <= 10; i++) {
executorService.submit(new MRunnable(i));
}
//关闭线程
List list = executorService.shutdownNow();
System.out.println("list = " + list);
此时的输出结果如下:
ScheduledExecutorService是ExecutorService的子接口,具备了延迟运行或定期执行任务的能力
常用获取方式如下:
1.static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
创建一个可重用固定线程数的线程池且允许延迟运行或定期执行任务;
2.static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
创建一个可重用固定线程数的线程池且线程池中的所有线程都使用ThreadFactory来创建,且允许延迟运行或定期执行任务;
3.static ScheduledExecutorService newSingleThreadScheduledExecutor()
创建一个单线程执行程序,它允许在给定延迟后运行命令或者定期地执行。
4.static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。
ScheduledExecutorService常用方法如下:
1.
延迟时间单位是unit,数量是delay的时间后执行callable。
2.ScheduledFuture> schedule(Runnable command, long delay, TimeUnit unit)
延迟时间单位是unit,数量是delay的时间后执行command。
3.ScheduledFuture> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
延迟时间单位是unit,数量是initialDelay的时间后,每间隔period时间重复执行一次command。
4.ScheduledFuture> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟。
scheduleAtFixedRate和scheduleWithFixedDelay的区别
参考:
- 并发系列(7)之 ScheduledThreadPoolExecutor 详解
1.scheduleAtFixedRate
可以看到如果任务执行时间超出周期时,下一次任务会立刻运行;就好像周期是一个有弹性的袋子,能装下运行时间的时候,是固定大小,装不下的时候就会被撑大,图像化表示如下:
2.scheduleWithFixedDelay
可以看到无论执行时间是多少,其结果都是在执行完毕后,停顿固定的时间,然后执行下一次任务,其图形化表示为:
如下简单延迟执行的例子:
public class ScheduleExecutorServiceDemo01 {
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
for (int i = 1; i <= 10; i++) {
scheduledExecutorService.schedule(new MyRunnable(i), 2, TimeUnit.SECONDS);
}
System.out.println("main over");
}
}
class MyRunnable implements Runnable {
private int id;
public MyRunnable(int id) {
this.id = id;
}
@Override
public void run() {
//获取线程的名称,并打印
String name = Thread.currentThread().getName();
System.out.println(name + "执行了任务..." + id);
}
}
运行后,控制台的输出结果为:
main over pool-1-thread-1执行了任务...1 pool-1-thread-2执行了任务...2 pool-1-thread-3执行了任务...3 pool-1-thread-1执行了任务...4 pool-1-thread-3执行了任务...5 pool-1-thread-3执行了任务...8 pool-1-thread-1执行了任务...7 pool-1-thread-2执行了任务...6 pool-1-thread-1执行了任务...10 pool-1-thread-3执行了任务...9



