zoukankan      html  css  js  c++  java
  • 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() 不阻塞。

     
     
     
     
    参考:
  • 相关阅读:
    os.path等os模块函数
    Eclipse 中 安装 SVN 插件
    Maven 库
    Activiti 学习笔记(2016-8-30)
    Mybatis 操作数据库的主键自增长
    将现有的sql脚本导入 Oracle 数据库,中文乱码问题
    oracle创建表之前判断表是否存在,如果存在则删除已有表
    Cannot change version of project facet Dynamic Web Module to 3.1
    【转】oracle数据库开发的一些经验积累
    Oracle的自增长主键
  • 原文地址:https://www.cnblogs.com/zyulike/p/13542206.html
Copyright © 2011-2022 走看看