java并发中ExecutorService的使用

简介: java并发中ExecutorService的使用

文章目录



java并发中ExecutorService的使用


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


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


创建ExecutorService


通常来说有两种方法来创建ExecutorService。


第一种方式是使用Executors中的工厂类方法,例如:


ExecutorService executor = Executors.newFixedThreadPool(10);


除了newFixedThreadPool方法之外,Executors还包含了很多创建ExecutorService的方法。


第二种方法是直接创建一个ExecutorService, 因为ExecutorService是一个interface,我们需要实例化ExecutorService的一个实现。


这里我们使用ThreadPoolExecutor来举例:


ExecutorService executorService =
            new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<Runnable>());


为ExecutorService分配Tasks


ExecutorService可以执行Runnable和Callable的task。其中Runnable是没有返回值的,而Callable是有返回值的。我们分别看一下两种情况的使用:


Runnable runnableTask = () -> {
    try {
        TimeUnit.MILLISECONDS.sleep(300);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
};
Callable<String> callableTask = () -> {
    TimeUnit.MILLISECONDS.sleep(300);
    return "Task's execution";
};


将task分配给ExecutorService,可以通过调用xecute(), submit(), invokeAny(), invokeAll()这几个方法来实现。


execute() 返回值是void,他用来提交一个Runnable task。


executorService.execute(runnableTask);


submit() 返回值是Future,它可以提交Runnable task, 也可以提交Callable task。 提交Runnable的有两个方法:


<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);


第一个方法在返回传入的result。第二个方法返回null。


再看一下callable的使用:


Future<String> future = 
  executorService.submit(callableTask);


invokeAny() 将一个task列表传递给executorService,并返回其中的一个成功返回的结果。


String result = executorService.invokeAny(callableTasks);


invokeAll() 将一个task列表传递给executorService,并返回所有成功执行的结果:


List<Future<String>> futures = executorService.invokeAll(callableTasks);


关闭ExecutorService


如果ExecutorService中的任务运行完毕之后,ExecutorService不会自动关闭。它会等待接收新的任务。如果需要关闭ExecutorService, 我们需要调用shutdown() 或者 shutdownNow() 方法。


shutdown() 会立即销毁ExecutorService,它会让ExecutorServic停止接收新的任务,并等待现有任务全部执行完毕再销毁。


executorService.shutdown();


shutdownNow()并不保证所有的任务都被执行完毕,它会返回一个未执行任务的列表:


List<Runnable> notExecutedTasks = executorService.shutdownNow();


oracle推荐的最佳关闭方法是和awaitTermination一起使用:


executorService.shutdown();
       try {
           if (!executorService.awaitTermination(800, TimeUnit.MILLISECONDS)) {
               executorService.shutdownNow();
           }
       } catch (InterruptedException e) {
           executorService.shutdownNow();
       }


先停止接收任务,然后再等待一定的时间让所有的任务都执行完毕,如果超过了给定的时间,则立刻结束任务。


Future


submit() 和 invokeAll() 都会返回Future对象。之前的文章我们已经详细讲过了Future。 这里就只列举一下怎么使用:


Future<String> future = executorService.submit(callableTask);
String result = null;
try {
   result = future.get();
} catch (InterruptedException | ExecutionException e) {
   e.printStackTrace();
}


ScheduledExecutorService


ScheduledExecutorService为我们提供了定时执行任务的机制。


我们这样创建ScheduledExecutorService:


ScheduledExecutorService executorService
                = Executors.newSingleThreadScheduledExecutor();


executorService的schedule方法,可以传入Runnable也可以传入Callable:


Future<String> future = executorService.schedule(() -> {
        // ...
        return "Hello world";
    }, 1, TimeUnit.SECONDS);
    ScheduledFuture<?> scheduledFuture = executorService.schedule(() -> {
        // ...
    }, 1, TimeUnit.SECONDS);


还有两个比较相近的方法:


scheduleAtFixedRate( Runnable command, long initialDelay, long period, TimeUnit unit )
scheduleWithFixedDelay( Runnable command, long initialDelay, long delay, TimeUnit unit )


两者的区别是前者的period是以任务开始时间来计算的,后者是以任务结束时间来计算。


ExecutorService和 Fork/Join


java 7 引入了Fork/Join框架。 那么两者的区别是什么呢?


ExecutorService可以由用户来自己控制生成的线程,提供了对线程更加细粒度的控制。


而Fork/Join则是为了让任务更加快速的执行完毕。


本文的代码请参考https://github.com/ddean2009/learn-java-concurrency/tree/master/ExecutorService

相关文章
|
4月前
|
安全 Java 编译器
揭秘JAVA深渊:那些让你头大的最晦涩知识点,从泛型迷思到并发陷阱,你敢挑战吗?
【8月更文挑战第22天】Java中的难点常隐藏在其高级特性中,如泛型与类型擦除、并发编程中的内存可见性及指令重排,以及反射与动态代理等。这些特性虽强大却也晦涩,要求开发者深入理解JVM运作机制及计算机底层细节。例如,泛型在编译时检查类型以增强安全性,但在运行时因类型擦除而丢失类型信息,可能导致类型安全问题。并发编程中,内存可见性和指令重排对同步机制提出更高要求,不当处理会导致数据不一致。反射与动态代理虽提供运行时行为定制能力,但也增加了复杂度和性能开销。掌握这些知识需深厚的技术底蕴和实践经验。
96 2
|
4月前
|
安全 Java 调度
解锁Java并发编程高阶技能:深入剖析无锁CAS机制、揭秘魔法类Unsafe、精通原子包Atomic,打造高效并发应用
【8月更文挑战第4天】在Java并发编程中,无锁编程以高性能和低延迟应对高并发挑战。核心在于无锁CAS(Compare-And-Swap)机制,它基于硬件支持,确保原子性更新;Unsafe类提供底层内存操作,实现CAS;原子包java.util.concurrent.atomic封装了CAS操作,简化并发编程。通过`AtomicInteger`示例,展现了线程安全的自增操作,突显了这些技术在构建高效并发程序中的关键作用。
75 1
|
28天前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
1月前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
1月前
|
Java 数据库连接 数据库
如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面
本文介绍了如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面。通过合理配置初始连接数、最大连接数和空闲连接超时时间,确保系统性能和稳定性。文章还探讨了同步阻塞、异步回调和信号量等并发控制策略,并提供了异常处理的最佳实践。最后,给出了一个简单的连接池示例代码,并推荐使用成熟的连接池框架(如HikariCP、C3P0)以简化开发。
51 2
|
2月前
|
Java
【编程进阶知识】揭秘Java多线程:并发与顺序编程的奥秘
本文介绍了Java多线程编程的基础,通过对比顺序执行和并发执行的方式,展示了如何使用`run`方法和`start`方法来控制线程的执行模式。文章通过具体示例详细解析了两者的异同及应用场景,帮助读者更好地理解和运用多线程技术。
34 1
|
3月前
|
Java API 容器
JAVA并发编程系列(10)Condition条件队列-并发协作者
本文通过一线大厂面试真题,模拟消费者-生产者的场景,通过简洁的代码演示,帮助读者快速理解并复用。文章还详细解释了Condition与Object.wait()、notify()的区别,并探讨了Condition的核心原理及其实现机制。
|
4月前
|
存储 Java
Java 中 ConcurrentHashMap 的并发级别
【8月更文挑战第22天】
60 5
|
4月前
|
存储 算法 Java
Java 中的同步集合和并发集合
【8月更文挑战第22天】
53 5
|
4月前
|
缓存 Java 调度
【Java 并发秘籍】线程池大作战:揭秘 JDK 中的线程池家族!
【8月更文挑战第24天】Java的并发库提供多种线程池以应对不同的多线程编程需求。本文通过实例介绍了四种主要线程池:固定大小线程池、可缓存线程池、单一线程线程池及定时任务线程池。固定大小线程池通过预设线程数管理任务队列;可缓存线程池能根据需要动态调整线程数量;单一线程线程池确保任务顺序执行;定时任务线程池支持周期性或延时任务调度。了解并正确选用这些线程池有助于提高程序效率和资源利用率。
61 2
下一篇
DataWorks