Java高并发编程中Executor、ExecutorService的使用及详细介绍-刘宇

作者:刘宇
CSDN博客地址:https://blog.csdn.net/liuyu973971883
有部分资料参考,如有侵权,请联系删除。如有不正确的地方,烦请指正,谢谢。

一、什么是Executor框架

Executor是从Java5中才开始有的,在java.util.cocurrent包下,是非常强大的异步执行框架,标准的将任务提交过程和执行过程进行解耦。内部使用了线程池机制,可以通过该框架来控制线程的启动、执行、关闭等,大大提高了并发开发的效率。

二、Executor框架示意图

在这里插入图片描述

  • Executor:是一个基本接口,提供方法execute(Runnable command),该方法接收一个Runable实例,将提交任务的过程与执行任务的过程进行了解耦。
  • ExecutorService:是一个比Executor使用更广泛的子类接口,其提供了生命周期管理的方法,以及可跟踪一个或多个异步任务执行状况返回Future的方法
  • AbstractExecutorService:ExecutorService执行方法的默认实现
  • ScheduledExecutorService:一个可定时调度任务的接口
  • ThreadPoolExecutor:线程池,可以通过调用Executors以下静态工厂方法来创建线程池并返回一个ExecutorService对象:
  • ScheduledThreadPoolExecutor:ScheduledExecutorService的实现,一个可定时调度任务的线程池

三、ExecutorService的方法详解

1、submit方法

//提交一个Runnable的任务,该任务没有返回结果,虽然返回一个Future对象,但是Future的get是null。
Future<?> submit(Runnable task)
//提交一个Runnable的任务,该任务可以有返回值,通过传入的result对象返回结果。
<T> Future<T> submit(Runnable task, T result)
//提交一个Callable的任务,该任务可以有返回值,因为Callable与Runnable不同,Callable自身就可以返回结果
<T> Future<T> submit(Callable<T> task)

2、invokeAny方法

该方法会阻塞。批量提交任务并执行所有callable,直到获得一个已经成功执行的任务的结果,如果一个任务已经完成,剩余的Callable任务将被取消即使它在运行也会被取消。

  • tasks:Callable类型的集合
  • timeout:超时时间
  • unit:时间单位

异常处理:

  • 如果Callable集合中只有部分Callable异常,则即使是将该异常抛出,在其调用的地方也是无法捕获异常的,因为该Callable异常了,则会调用Callable集合中的下一个Callable。
  • 如果Callable集合中的Callable全部异常,则可以在其调用的地方捕获异常的。
<T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;

3、invokeAll方法

该方法会阻塞。批量提交任务并获得他们的future,Task列表与Future列表一一对应

  • tasks:Callable类型的集合
  • timeout:超时时间
  • unit:时间单位

异常处理

  • 当线程池中线程发送发生异常时,直接在抛出,可以在其调用的方法捕获异常,但是只有在调用Future中的get才能捕获异常,否则则一样捕获不到。
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;

4、awaitTermination方法

是阻塞方法。等待一定时间后如果有任务未结束则强行关闭

boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;

5、isShutdown方法

判断该Executor是否关闭,调用shutdown后会变为true,即使Executor还没有关闭。

boolean isShutdown();

6、shutdown方法

是非阻塞方法。不再接受新的任务,等待所有任务执行完毕后关闭Excutor

void shutdown();

7、shutdownNow方法

  • 它会对正在执行的线程进行interrupt,也有可能存在interrupt不成功的现象,如该线程中没有sleep、wait、join等即没有抛出中断异常的方法,则可能会出现中断失败
  • 将空闲线程取消
  • 会返回未执行的任务列表
List<Runnable> shutdownNow()

8、isTerminated方法

是非阻塞方法。判断是否Executor是否已终结,调用shutdown后该方法并不会立即返回true,只有等到所有任务都结束,Executor真正结束时才返回true

boolean isTerminated()

四、ExecutorService练习

Executors工具类这里只要理解为快建创建线程池的工具类就可以了,Executors的使用详情请看后面的博客

1、如何捕获线程执行过程中出现的异常并处理

1.1 利用Thread中uncaughtExceptionHandler来处理

  • 弊端:捕获异常时无法得知实体任务中的属性。
package com.test.part3.threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

public class ExecutorServiceExample {

    public static void main(String[] args) {
    	//创建一个core和最大线程数都为5,空闲时间为0的线程池,并且指定一个我们自己的ThreadFactory
        ExecutorService executorService = Executors.newFixedThreadPool(5,new ThreadFactory());
        IntStream.rangeClosed(1,5).boxed().forEach(i->{
            executorService.execute(()->{
                System.out.println(1/0);
            });
        });
    }
    private static class ThreadFactory implements java.util.concurrent.ThreadFactory {
        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            //对线程中的异常进行捕获,但这种情况并不能得知线程任务中的属性
            t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
                @Override
                public void uncaughtException(Thread t, Throwable e) {
                    System.out.println(t.getName()+": 发生了异常");
                    e.printStackTrace();
                }
            });
            return t;
        }
    }
}

运行结果

Thread-0: 发生了异常
Thread-2: 发生了异常
Thread-1: 发生了异常
Thread-3: 发生了异常
Thread-4: 发生了异常
java.lang.ArithmeticException: / by zero
	at com.test.part3.threadpool.ExecutorServiceExample.lambda$null$0(ExecutorServiceExample.java:15)
	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)
java.lang.ArithmeticException: / by zero
	at com.test.part3.threadpool.ExecutorServiceExample.lambda$null$0(ExecutorServiceExample.java:15)
	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)
java.lang.ArithmeticException: / by zero
	at com.test.part3.threadpool.ExecutorServiceExample.lambda$null$0(ExecutorServiceExample.java:15)
	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)
java.lang.ArithmeticException: / by zero
	at com.test.part3.threadpool.ExecutorServiceExample.lambda$null$0(ExecutorServiceExample.java:15)
	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)
java.lang.ArithmeticException: / by zero
	at com.test.part3.threadpool.ExecutorServiceExample.lambda$null$0(ExecutorServiceExample.java:15)
	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)

1.2 定义一个抽象的实现了Runnable接口的类来处理

package com.test.part3.threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

public class ExecutorServiceExample {

    public static void main(String[] args) {
    	//创建一个core和最大线程数都为5的线程池,且该线程池的空闲时间为0
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        IntStream.rangeClosed(1,5).boxed().forEach(i->{
            executorService.execute(new MyTask(i) {
                @Override
                protected void doError(Throwable cause) {
                    System.out.println(Thread.currentThread().getName()+": 发生了异常, 编号: "+super.no);
                    cause.printStackTrace();
                }
            });
        });
    }
    private abstract static class MyTask implements Runnable{
        private int no;

        public MyTask(int no) {
            this.no = no;
        }

        @Override
        public void run() {
            try {
                //模拟做事情
                TimeUnit.SECONDS.sleep(1);
                System.out.println("编号:"+no+" 准备开始工作");
                //模拟做到一半时部分出现了异常
                if(no%3==0){
                    int num = 1/0;
                }
                System.out.println("编号:"+no+" 完成工作");
            } catch (Exception e) {
                doError(e);
            }
        }
        //交由外部实现
        protected abstract void doError(Throwable cause);
    }
}

输出结果:

编号:2 准备开始工作
编号:1 准备开始工作
编号:3 准备开始工作
编号:1 完成工作
编号:2 完成工作
pool-1-thread-3: 发生了异常, 编号: 3
编号:4 准备开始工作
编号:4 完成工作
编号:5 准备开始工作
编号:5 完成工作
java.lang.ArithmeticException: / by zero
	at com.test.part3.threadpool.ExecutorServiceExample$MyTask.run(ExecutorServiceExample.java:44)
	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)
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaExecutorService提供了awaitTermination()方法来等待所有线程执行完成。该方法会阻塞当前线程,直到所有线程执行完成或者超时。下面是一个示例代码: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class WaitAllThreadsDemo { private static ExecutorService executor = Executors.newFixedThreadPool(2); public static void main(String[] args) throws InterruptedException { executor.submit(new Runnable() { @Override public void run() { System.out.println("Thread 1 started"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread 1 ended"); } }); executor.submit(new Runnable() { @Override public void run() { System.out.println("Thread 2 started"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread 2 ended"); } }); executor.shutdown(); executor.awaitTermination(10, TimeUnit.SECONDS); System.out.println("All threads ended"); } } ``` 在上面的示例代码,我们创建了一个ExecutorService对象,并使用submit()方法提交了两个Runnable任务。然后,我们调用ExecutorService.shutdown()方法来关闭线程池,并使用ExecutorService.awaitTermination()方法来等待所有线程执行完成。我们设置了10秒的超时时间,如果超过该时间仍有线程没有执行完成,则会抛出InterruptedException异常。 需要注意的是,在使用ExecutorService等待所有线程执行完成时,要注意线程之间的同步问题,以避免出现线程安全问题。同时,也要注意设置合适的超时时间,以避免等待时间过长导致程序阻塞。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值