一文搞懂Executor执行器和线程池的关系,整体介绍其任务执行/调度体系:ThreadPoolExecutor、ScheduledExecutorService(中)

简介: 一文搞懂Executor执行器和线程池的关系,整体介绍其任务执行/调度体系:ThreadPoolExecutor、ScheduledExecutorService(中)

AbstractExecutorService


ExecutorService的抽象实现:它实现了接口的部分方法,但是它并没有存放任务或者线程的数组或者Collection,也就是说它依旧和线程池没有半毛钱关系


// @since 1.5
public abstract class AbstractExecutorService implements ExecutorService {
  @Override
    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }
    ... // 其它submit方法原理一样,底层执行调用的均是Executor#execute方法
}


关于invokeAll()/invokeAny()等方法的执行源码此处就不铺开了,记住结论即可:批量执行时使用特别方便(注意全部成功or任意一个成功的区别)。


手写实现AbstractExecutorService


本文以一个非常简单手写实例来告诉你任务执行器的效果,进一步让你感受到它目前还和线程池木有半毛钱关系。


private static class MyExecutorService extends AbstractExecutorService {
    @Override
    public void shutdown() {
        System.out.println("关闭执行器,释放资源");
    }
    @Override
    public List<Runnable> shutdownNow() {
        System.out.println("立刻关闭执行器,释放资源");
        return Collections.emptyList();
    }
    @Override
    public boolean isShutdown() {
        return false;
    }
    @Override
    public boolean isTerminated() {
        return false;
    }
    @Override
    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
        return false;
    }
    // 执行任务(本处使用异步执行)
    @Override
    public void execute(Runnable command) {
        new Thread(command).start();
    }
}


准备一个方法,用于产生任务


// period:任务执行耗时 单位s
private Runnable createTask(int period) {
    return () -> {
        try {
            TimeUnit.SECONDS.sleep(period);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String currThreadName = Thread.currentThread().getName();
        System.out.println("线程[" + currThreadName + "] 我是异步执行的,耗时" + period + "s");
    };
}


书写测试方法:


@Test
public void fun3() throws InterruptedException, ExecutionException {
    String mainThreadName = Thread.currentThread().getName();
    System.out.println("----------主线程[" + mainThreadName + "]开始----------");
    ExecutorService executorService = new MyExecutorService();
    Instant start = Instant.now();
    Future<?> submit = executorService.submit(createTask(3));
    System.out.println("结果为:" + submit.get());
    Instant end = Instant.now();
    System.out.println("总耗时为:" + Duration.between(start, end).getSeconds());
    executorService.shutdown();
}


运行程序,打印:


----------主线程[main]开始----------
线程[Thread-0] 我是异步执行的,耗时3s
结果为:null
总耗时为:0
关闭执行器,释放资源


下面仅需改一下,把get放在上面:

System.out.println("结果为:" + submit.get());
Instant end = Instant.now();
System.out.println("总耗时为:" + Duration.between(start, end).getSeconds());


再次运行打印:


----------主线程[main]开始----------
线程[Thread-0] 我是异步执行的,耗时3s
结果为:null
总耗时为:3
关闭执行器,释放资源


另外,关于其它submit方法,以及批量执行的invokeAll/invokeAny方法,各位可自行测试哈。下面介绍JDK自带的,Doug Lea大神给我们提供的实现类,也是最最最最最为重要的一个类:ThreadPoolExecutor。


ThreadPoolExecutor 带线程池的执行器


顾名思义,它是一个内置线程池的执行器,也就是说:它会把Runnable任务的执行均扔进线程池里面进行执行,效率最高。


注意:ThreadPoolTaskExecutor它是Spirng提供的,基于ThreadPoolExecutor进行包装实现,请勿弄混了。


本文并不会解释为何需要线程池,以及构建线程池的七大参数都是什么意思,而只会站在使用以及基础原理的角度做出示例和说明。


public class ThreadPoolExecutor extends AbstractExecutorService {
  // 核心线程数
  private volatile int corePoolSize;
  // 最大线程数
  private volatile int maximumPoolSize;
  // 任务队列(当任务太多了,就放在这里排队)
  private final BlockingQueue<Runnable> workQueue;
  // 空闲线程的超时时间,超时就回收它(非core线程)
  private volatile long keepAliveTime;
  // 不解释
  private volatile ThreadFactory threadFactory;
  // 线程池拒绝处理函数(如任务太多了,如何拒绝)
  // 默认策略是:AbortPolicy。要决绝是抛出异常:RejectedExecutionException
  private volatile RejectedExecutionHandler handler;
}


通过上里手工模拟可知道最重要的是对execute()方法的实现:它决定了你最终如何去执行任务(同步or异步?用老的线程还是用新的线程等等)。


相关文章
|
27天前
|
算法 安全 Java
Java线程调度揭秘:从算法到策略,让你面试稳赢!
在社招面试中,关于线程调度和同步的相关问题常常让人感到棘手。今天,我们将深入解析Java中的线程调度算法、调度策略,探讨线程调度器、时间分片的工作原理,并带你了解常见的线程同步方法。让我们一起破解这些面试难题,提升你的Java并发编程技能!
68 16
|
1月前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
110 17
|
3月前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
77 12
|
3月前
|
消息中间件 监控 Java
线程池关闭时未完成的任务如何保证数据的一致性?
保证线程池关闭时未完成任务的数据一致性需要综合运用多种方法和机制。通过备份与恢复、事务管理、任务状态记录与恢复、数据同步与协调、错误处理与补偿、监控与预警等手段的结合,以及结合具体业务场景进行分析和制定策略,能够最大程度地确保数据的一致性,保障系统的稳定运行和业务的顺利开展。同时,不断地优化和改进这些方法和机制,也是提高系统性能和可靠性的重要途径。
143 62
|
3月前
|
存储 Java 数据库
如何处理线程池关闭时未完成的任务?
总之,处理线程池关闭时未完成的任务需要综合考虑多种因素,并根据实际情况选择合适的处理方式。通过合理的处理,可以最大程度地减少任务丢失和数据不一致等问题,确保系统的稳定运行和业务的顺利开展。
173 64
|
3月前
|
开发框架 Java .NET
.net core 非阻塞的异步编程 及 线程调度过程
【11月更文挑战第12天】本文介绍了.NET Core中的非阻塞异步编程,包括其基本概念、实现方式及应用示例。通过`async`和`await`关键字,程序可在等待I/O操作时保持线程不被阻塞,提高性能。文章还详细说明了异步方法的基础示例、线程调度过程、延续任务机制、同步上下文的作用以及如何使用`Task.WhenAll`和`Task.WhenAny`处理多个异步任务的并发执行。
|
4月前
|
安全 调度 C#
STA模型、同步上下文和多线程、异步调度
【10月更文挑战第19天】本文介绍了 STA 模型、同步上下文和多线程、异步调度的概念及其优缺点。STA 模型适用于单线程环境,确保资源访问的顺序性;同步上下文和多线程提高了程序的并发性和响应性,但增加了复杂性;异步调度提升了程序的响应性和资源利用率,但也带来了编程复杂性和错误处理的挑战。选择合适的模型需根据具体应用场景和需求进行权衡。
|
4月前
|
缓存 负载均衡 Java
c++写高性能的任务流线程池(万字详解!)
本文介绍了一种高性能的任务流线程池设计,涵盖多种优化机制。首先介绍了Work Steal机制,通过任务偷窃提高资源利用率。接着讨论了优先级任务,使不同优先级的任务得到合理调度。然后提出了缓存机制,通过环形缓存队列提升程序负载能力。Local Thread机制则通过预先创建线程减少创建和销毁线程的开销。Lock Free机制进一步减少了锁的竞争。容量动态调整机制根据任务负载动态调整线程数量。批量处理机制提高了任务处理效率。此外,还介绍了负载均衡、避免等待、预测优化、减少复制等策略。最后,任务组的设计便于管理和复用多任务。整体设计旨在提升线程池的性能和稳定性。
122 5
|
5月前
|
存储 Java 数据处理
进程中的线程调度
进程是应用程序运行的基本单位,包括主线程、用户线程和守护线程。计算机由存储器和处理器协同操作,操作系统设计为分时和分任务模式。在个人PC普及后,基于用户的时间片异步任务操作系统确保了更好的体验和性能。线程作为进程的调度单元,通过覆写`Thread`类的`run`方法来处理任务数据,并由系统调度框架统一管理。微服务架构进一步将应用分解为多个子服务,在不同节点上执行,提高数据处理效率与容错性,特别是在大规模数据存储和处理中表现显著。例如,利用微服务框架可以优化算法,加速业务逻辑处理,并在不同区块间分配海量数据存储任务。
|
6月前
|
前端开发 JavaScript 大数据
React与Web Workers:开启前端多线程时代的钥匙——深入探索计算密集型任务的优化策略与最佳实践
【8月更文挑战第31天】随着Web应用复杂性的提升,单线程JavaScript已难以胜任高计算量任务。Web Workers通过多线程编程解决了这一问题,使耗时任务独立运行而不阻塞主线程。结合React的组件化与虚拟DOM优势,可将大数据处理等任务交由Web Workers完成,确保UI流畅。最佳实践包括定义清晰接口、加强错误处理及合理评估任务特性。这一结合不仅提升了用户体验,更为前端开发带来多线程时代的全新可能。
157 1