Java 多线程使用 ExecutorService

ExecutorService

java并发中ExecutorService的使用

ExecutorService是java中的一个异步执行的框架,通过使用ExecutorService可以方便的创建多线程执行环境。

本文将会详细的讲解ExecutorService的具体使用。

创建ExecutorService

Executors 提供了一系列工厂方法用于创先线程池,返回的线程池都实现了 ExecutorService 接口。

/* 
 * 该方法返回一个固定线程数量的线程池,该线程池池中的线程数量始终不变。
 * 当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。
 * 若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务 
 * 默认等待队列长度为Integer.MAX_VALUE
 */
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(1);

/* 
 * 该方法返回一个只有一个线程的线程池。
 * 若多余一个任务被提交到线程池,任务会被保存在一个任务队列中,等待线程空闲,按先入先出顺序执行队列中的任务
 * 默认等待队列长度为Integer.MAX_VALUE
 */
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

/* 
 * 该方法返回一个可根据实际情况调整线程数量的线程池。
 * 线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。
 * 若所有线程均在工作,又有新任务的提交,则会创建新的线程处理任务。
 * 所有线程在当前任务执行完毕后,将返回线程池进行复用
 */
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();

/* 
 * 该方法返回一个ScheduledExecutorService对象,线程池大小为1。
 * ScheduledExecutorService接口在ExecutorService接口之上扩展了在给定时间内执行某任务的功能,
 * 如在某个固定的延时之后执行,或者周期性执行某个任务
 */
ExecutorService newSingleThreadScheduledExecutor = Executors.newSingleThreadScheduledExecutor();

/*
 * 该方法也返回一个ScheduledExecutorService对象,但该线程池可以指定线程数量
 */
ExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(1);

ExecutorService 使用方法

这里有几种不同的方式让你将任务委托给壹個 ExecutorService。
  • execute(Runnable)
executorService.execute(new Runnable() {  
    public void run() {  
        System.out.println("Asynchronous task");  
    }  
});
  • Future submit(Runnable)
同样接收一个 Runnable 的实现作为参数,但是会返回一個 Future 对象。这個 Future 对象可以用于判断 Runnable 是否结束执行。
Future future = executorService.submit(new Runnable() {
    public void run() {
        System.out.println("Asynchronous task");
    }
});

// 如果任务结束执行则返回 null
System.out.println("future.get()=" + future.get());
  • Future submit(Callable)

Callable 的 call() 方法可以返回一个结果。Callable 的返回值可以从方法 submit(Callable) 返回的 Future 对象中获取。

Future future = executorService.submit(new Callable(){
    public Object call() throws Exception {
        System.out.println("Asynchronous Callable");
        return "Callable Result";
    }
});
System.out.println("future.get() = " + future.get());
  • T invokeAny(Collection<Callable>)

任务集合中,任何一个任务完成就返回。

接收Callable 对象的集合作为参数。调用该方法不会返回 Future 对象,而是返回集合中某一個 Callable 对象的结果。

无法保证调用之后返回的结果是哪個 Callable。

ExecutorService executorService = Executors.newSingleThreadExecutor();
Set<Callable<String>> callables = new HashSet<Callable<String>>();
callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 1";
    }
});
callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 2";
    }
});
callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 3";
    }
});
String result = executorService.invokeAny(callables);
System.out.println("result = " + result);
executorService.shutdown();
  • List<Future> invokeAll(Collection<Callable>)

同步的方法,执行所有的任务列表,当所有任务都执行完成后,返回Future列表。

执行Runnable任务

通过 Executors 的以上四个静态工厂方法获得 ExecutorService 实例,而后调用该实例的 execute(Runnable command)方法即可。一旦 Runnable 任务传递到 execute()方法,该方法便会自动在一个线程上执行。下面是 Executor 执行 Runnable 任务的示例代码:
public static void main(String[] args) {
    ExecutorService executorService = Executors.newFixedThreadPool(3);

    for (int i = 0; i < 5; i++) {

        int finalI = i;
        executorService.execute(new Runnable() {
            @Override
            public void run() {
               try {
                    // 休眠is
                 TimeUnit.SECONDS.sleep(finalI);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println(Thread.currentThread().getName() + "线程被调用了。" + new Date());
          }
      });
    }
}

执行结果如下:

pool-1-thread-1线程被调用了。Fri Aug 21 14:47:33 CST 2020
pool-1-thread-2线程被调用了。Fri Aug 21 14:47:34 CST 2020
pool-1-thread-3线程被调用了。Fri Aug 21 14:47:35 CST 2020
pool-1-thread-1线程被调用了。Fri Aug 21 14:47:36 CST 2020
pool-1-thread-2线程被调用了。Fri Aug 21 14:47:38 CST 2020

从结果中可以看出,pool-1-thread-1和pool-1-thread-2均被调用两次,我们设定的线程数量为3个,如果线程池中没有空闲的线程,它会等待之前运行的线程结束后再执行任务

ExecutorService 服务关闭

当使用 ExecutorService 完毕之后,我们应该关闭它。 例如:你的程序通过 main() 方法启动,并且主线程退出了你的程序,如果你还有壹個活动的 ExecutorService 存在于你的程序中,那么程序将会继续保持运行状态。存在于 ExecutorService 中的活动线程会阻止Java虚拟机关闭。
ExecutorService 的三个关闭方法(shutdown()shutdownNow()awaitTermination()

shutdown()

将线程池状态置为 SHUTDOWN,并不会立即停止:

  • 停止接收外部submit的任务
  • 内部正在跑的任务和队列里等待的任务,会执行完
  • 等到第二步完成后,才真正停止

shutdownNow()

将线程池状态置为STOP。企图立即停止,事实上不一定:

  • 跟shutdown()一样,先停止接收外部提交的任务
  • 忽略队列里等待的任务
  • 尝试将正在跑的任务interrupt中断
  • 返回未执行的任务列表

它试图终止线程的方法是通过调用Thread.interrupt()方法来实现的,但是大家知道,这种方法的作用有限,如果线程中没有sleep 、wait、Condition、定时锁等应用, interrupt()方法是无法中断当前的线程的。所以,ShutdownNow()并不代表线程池就一定立即就能退出,它也可能必须要等待所有正在执行的任务都执行完成了才能退出。

但是大多数时候是能立即退出的

 

awaitTermination(long timeOut, TimeUnit unit)

当前线程阻塞,直到:

  • 等所有已提交的任务(包括正在跑的和队列中等待的)执行完;
  • 或者 等超时时间到了(timeout 和 TimeUnit设定的时间);
  • 或者 线程被中断,抛出InterruptedException

一般配合 shutdown() 使用

 
 

1、shutdown() 和 shutdownNow() 的区别

shutdown() 只是关闭了提交通道,用submit()是无效的;而内部该怎么跑还是怎么跑,跑完再停。
shutdownNow() 能立即停止线程池,正在跑的和正在等待的任务都停下了。
 

2、shutdown() 和 awaitTermination() 的区别

shutdown() 后,不能再提交新的任务进去;但是 awaitTermination() 后,可以继续提交。

awaitTermination()是阻塞的,返回结果是线程池是否已停止(true/false);shutdown() 不阻塞。

 
 
 
 
参考:
原文地址:https://www.cnblogs.com/zyulike/p/13542206.html