首页 > 其他分享 >并发工具类:ExecutorService、Future、CountDownLatch与Semaphore(第一章)

并发工具类:ExecutorService、Future、CountDownLatch与Semaphore(第一章)

时间:2024-04-08 16:01:39浏览次数:22  
标签:CountDownLatch 并发 任务 Future 线程 提交 Semaphore ExecutorService

目录

一、引言

ExecutorService与Future:优雅的任务提交与结果获取

CountDownLatch:精确的线程同步点

Semaphore:资源访问的流量控制器

总结

二、ExecutorService

定义与接口概述

生命周期管理

高级特性与最佳实践

使用ExecutorService时的常见注意事项与最佳实践建议


一、引言

在当今多核处理器普及的背景下,多线程编程已成为提升软件性能、充分利用硬件资源的关键手段。然而,多线程编程也伴随着一系列复杂问题,如线程同步、资源争抢、死锁等,这些问题若处理不当,将严重影响程序的正确性和稳定性。为此,Java平台通过其强大的并发工具类库为开发者提供了高效且易于使用的解决方案,其中ExecutorServiceFutureCountDownLatchSemaphore尤为突出,它们在Java多线程编程中扮演着至关重要的角色。

ExecutorService与Future:优雅的任务提交与结果获取

ExecutorService作为Java并发框架的核心接口,提供了一种统一的方式来管理和控制线程池。它封装了线程的创建、调度、监控等复杂细节,使开发者无需直接操作底层线程,只需提交RunnableCallable任务即可。这种抽象极大地简化了多线程编程,提升了代码的可读性和可维护性。ExecutorService支持异步任务执行、任务队列管理、线程池大小调整等功能,能够根据应用需求灵活配置和调整并发策略。

Future接口则与ExecutorService紧密配合,作为异步任务执行的结果容器。当提交一个Callable任务到ExecutorService时,会得到一个Future对象,通过它可以在任务完成后查询结果、检查是否已完成、或者阻塞等待结果。Future机制使得主线程能够异步地等待任务完成,避免了传统同步方法可能导致的阻塞和浪费资源,同时也便于实现任务取消、超时处理等高级功能。

CountDownLatch:精确的线程同步点

CountDownLatch作为一个灵活的同步工具,常用于控制一组线程之间的等待关系。它允许一个或多个线程等待其他线程完成指定数量的任务。初始化时设定一个计数值,每当一个任务完成,调用countDown()方法递减计数;当计数值减至零时,所有等待的线程将被await()方法释放,从而继续执行。CountDownLatch特别适用于一次性事件的同步,如启动阶段等待所有初始化任务完成,或是测试场景中模拟并发访问的并发点控制。

Semaphore:资源访问的流量控制器

Semaphore作为一种经典的信号量实现,用于控制对共享资源的并发访问权限。它维护一个许可计数,通过acquire()方法获取许可(若无可用许可则可能阻塞),release()方法释放许可。Semaphore可以限制同时访问特定资源的线程数量,有效防止过多线程同时进入临界区导致的资源争抢或系统过载。在诸如数据库连接池、并发下载限制、并发用户数控制等场景中,Semaphore能够确保系统的稳定性和服务质量。

总结

综上所述,ExecutorServiceFutureCountDownLatchSemaphore这四个并发工具类共同构成了Java多线程编程的强大基石。它们不仅有效地解决了线程同步、任务调度、并发控制等典型并发问题,还极大地提高了编程效率和代码质量,使开发者能够更专注于业务逻辑实现,而不必深陷于复杂的线程管理细节之中。熟练运用这些工具类,将极大地提升Java应用程序的并发性能与可靠性。

二、ExecutorService

定义与接口概述

ExecutorService是Java并发包(java.util.concurrent)中一个核心接口,它在管理线程池和执行异步任务方面起着至关重要的作用。ExecutorService为开发者提供了一个统一、灵活且易于使用的接口,用于创建、管理和控制线程池,执行并监控异步任务的执行状态。

功能定位

  1. 线程池管理ExecutorService作为线程池的抽象,封装了线程的创建、调度、监控和回收等复杂操作。通过使用ExecutorService,开发者无需直接操作底层线程,而是将关注点集中在任务的提交和结果处理上,大大简化了多线程编程的复杂性。

  2. 异步任务执行ExecutorService支持提交RunnableCallable任务进行异步执行。提交的任务会被放入线程池的工作队列中,由线程池中的工作线程按照预定策略取出并执行。这种方式允许主线程在提交任务后立即返回,无需等待任务完成,从而提高程序的响应速度和并发能力。

  3. 任务监控与控制ExecutorService提供了诸如任务完成通知、线程池状态查询、线程池关闭等方法,使得开发者能够监控任务执行进度,适时干预线程池行为,确保程序的稳定性和资源的有效利用。

创建与使用

通过Executors类创建ExecutorService实例

Java提供了便捷的Executors类,它包含了多种预定义的ExecutorService工厂方法,可以根据不同的应用场景快速创建所需的线程池。

固定大小线程池Executors.newFixedThreadPool(int nThreads)创建一个固定大小的线程池,池中的线程数量始终保持不变。当有新任务提交时,若所有线程都在工作,则任务进入队列等待。适用于任务数量较为稳定且对响应时间有要求的场景。

示例代码:

ExecutorService executor = Executors.newFixedThreadPool(10);  // 创建一个包含10个工作线程的线程池

单线程ExecutorExecutors.newSingleThreadExecutor()创建一个仅包含一个工作线程的线程池。所有提交的任务按照提交顺序逐个执行,保证了任务的串行化执行。适用于任务之间存在依赖关系或需要严格保证执行顺序的场景。

示例代码:

ExecutorService executor = Executors.newSingleThreadExecutor();  // 创建一个单线程线程池

使用submit()方法提交任务并获取Future对象

ExecutorServicesubmit()方法用于提交RunnableCallable任务,并返回一个Future对象。Future接口代表异步计算的结果,提供了查询任务执行状态、获取计算结果以及取消任务的方法。

提交Runnable任务:若提交Runnable任务,submit()返回一个Future<?>对象。由于Runnable没有返回值,调用Future.get()方法会立即返回null

示例代码:

Future<?> future = executor.submit(() -> {
    // 执行耗时任务
});

提交Callable任务:若提交Callable<T>任务,submit()返回一个Future<T>对象。调用Future.get()方法会阻塞等待任务完成,然后返回Callable的计算结果。若任务抛出异常,get()会将该异常封装为ExecutionException抛出。

示例代码:

Future<Integer> future = executor.submit(() -> {
    // 执行耗时任务并返回结果
    return someComputation();
});

try {
    Integer result = future.get();  // 阻塞等待任务完成,返回结果
    System.out.println("Result: " + result);
} catch (InterruptedException | ExecutionException e) {
    // 处理异常
}

生命周期管理

shutdown()与shutdownNow()方法的区别

shutdown():启动正常关闭序列,停止接收新任务,但已提交的任务将继续执行直至完成。调用此方法后,ExecutorService将不再接受新任务,现有任务执行完毕后,线程池进入终止状态。可以通过awaitTermination()方法等待线程池完全终止。

示例代码:

executor.shutdown();  // 启动关闭序列
executor.awaitTermination(5, TimeUnit.SECONDS);  // 等待最多5秒,直到线程池终止或超时

监控ExecutorService状态

isTerminated():判断线程池是否已经完全终止,即所有提交的任务都已完成,且不再接受新任务。

示例代码:

if (executor.isTerminated()) {
    System.out.println("Executor has terminated.");
}

awaitTermination(long timeout, TimeUnit unit):阻塞当前线程,直到线程池终止或等待时间超过指定的超时值。若线程池在超时期限内终止,返回true;否则返回false

示例代码:

boolean terminated = executor.awaitTermination(10, TimeUnit.SECONDS);  // 等待最多10秒,直到线程池终止或超时
System.out.println("Executor terminated: " + terminated);

高级特性与最佳实践

其他重要方法:invokeAll()、invokeAny()

invokeAll(Collection<? extends Callable<T>> tasks):提交一组Callable任务并阻塞,直到所有任务都完成。返回一个包含每个任务结果的List<Future<T>>,按照任务提交顺序排列。适用于需要等待所有任务完成后再进行下一步处理的场景。

示例代码:

List<Callable<String>> tasks = Arrays.asList(
    () -> "Task 1 result",
    () -> "Task 2 result"
);

List<Future<String>> results = executor.invokeAll(tasks);

for (Future<String> future : results) {
    System.out.println(future.get());  // 获取并打印每个任务的结果
}

invokeAny(Collection<? extends Callable<T>> tasks):提交一组Callable任务并阻塞,直到其中任意一个任务完成。返回完成任务的结果。适用于只需一个任务结果,或者多个任务有互相覆盖效果的场景。

示例代码:

List<Callable<String>> tasks = Arrays.asList(
    () -> "Task 1 result",
    () -> "Task 2 result"
);

String result = executor.invokeAny(tasks);
System.out.println("First completed task result: " + result);

使用ExecutorService时的常见注意事项与最佳实践建议

  1. 合理设置线程池大小:根据系统的CPU核心数、任务性质(CPU密集型还是IO密集型)、任务间的依赖关系等因素,选择合适的线程池大小。避免线程池过大导致过多线程竞争CPU资源,或过小导致任务积压和响应时间增加。

  2. 处理任务异常:对于使用submit()提交的Callable任务,应捕获并妥善处理Future.get()方法抛出的InterruptedExceptionExecutionException。对于使用execute()提交的Runnable任务,应在任务内部处理可能抛出的异常。

  3. 资源清理:确保在程序退出前正确关闭ExecutorService,避免资源泄漏。可以使用try-finallytry-with-resources结构确保关闭操作的执行。

  4. 监控与调优:定期检查线程池的状态(如活动线程数、队列长度、已完成任务数等),根据监控数据调整线程池参数或任务处理逻辑。使用ThreadPoolExecutor的自定义版本时,可以设置饱和策略、自定义工作队列等,以适应特定应用场景。

通过深入理解和熟练运用ExecutorService的各项功能,开发者可以构建出高效、稳定且易于管理的多线程应用程序,充分发挥Java并发编程的优势。

标签:CountDownLatch,并发,任务,Future,线程,提交,Semaphore,ExecutorService
From: https://blog.csdn.net/m0_61635718/article/details/137377011

相关文章

  • C++多线程:async、future、packaged_task、promise、shared_future的学习与使用(九)
    1、异步任务线程异步线程的概念:异步:就是非同步,同步就是必须一个一个的执行,异步可以两个事情一起干异步线程:异步线程就相当于把非关联的两件事分开找两个线程去执行,而分开的那个就是异步线程举例:例如登录信息,用户登录完毕主线程肯定是需要去及时响应用户的请求的,而系统设......
  • 在Python中用concurrent.futures创建线程池进程池
    简介Python3.2带来了concurrent.futures模块,借此能够快速使用线程池和进程池。对于不需要控制优先级与资源分配的多任务,使用concurrent.futures模块快捷优雅。示例代码与效果importconcurrent.futuresimporttimedefa_task(x):"""模拟一个耗时的任务"""de......
  • Java中的信号量Semaphore
    引言:        在Java编程中,信号量Semaphore是一种重要的同步工具,用于控制对共享资源的访问。本文将详细介绍信号量Semaphore的概念、用法以及一些示例。一、什么是信号量Semaphore?        信号量Semaphore是一种用于控制多个线程访问共享资源的同步工具。......
  • 手把手教你做阅读理解题-初中中考阅读理解解题技巧007-Mapping out the future
    手把手教你做阅读理解题-初中中考阅读理解解题技巧007-MappingoutthefuturePDF格式公众号回复关键字:ZKYD007阅读理解技巧,在帮助读者有效获取和理解文本信息方面发挥着重要作用,熟练掌握如下6个技巧,可快速突破阅读理解1预览文章结构在开始深入阅读之前,快速浏览文章的标......
  • CompletableFuture 异步编排的简单使用
    目录1、创建异步对象2、计算完成时回调方法3、handle方法4、线程串行化方法5、两任务组合-都要完成6、两任务组合-一个完成7、多任务组合如果在我们的业务中某些功能需要其他一些功能执行完成之后才能开始执行(比如获取其他功能的返回结果),这样就需要用到异步编排......
  • future
    C++11提供了std::future类模板,future对象提供访问异步操作结果的机制,很轻松解决从异步任务中返回结果。在C++标准库中,有两种“期望”,使用两种类型模板实现(这里主要介绍的是唯一期望):唯一期望(uniquefutures,std::future<>)实例只能与一个指定事件相关联。共享期望(shared......
  • 【CountDownLatch】
    CountDownLatch是一个计数器,允许一个或多个线程等待某些操作完成后在同时执行。当计数器等于0时,等待的线程才可以同时执行。例如:CountDownLatchstatus=newCountDownLatch(5);意味着定义了5个计数器的CountDownLatch;主要方法:await方法:当线程调用该方法后,线程会立即处......
  • CompletableFuture概述、创建方式、常用API、电商比价需求
    ①.CompletableFuture概述②.CompletableFuture创建方式③.CompletableFutureAPI①.获得结果和触发计算(get、getNow、join、complete)②.对计算结果进行处理(thenApply、handle)③.对计算结果进行消费(thenRun、thenAccept、thenApply)④.对计算速度进行选用(appl......
  • 中考英语首字母快速突破014-2021上海徐汇英语二模-Future Changes: Predictions and P
    PDF格式公众号回复关键字:ZKSZM014原文​Readthecommentsaboutchangesinthefuture.Howmuchdoyouagreewiththem?​Thedays,somepeopleworkathomeoneortwodaysaweekinsteadofgoingtoanofficeeveryday.Ithinkinthefuture......
  • Semaphore源码解析
    Semaphorehttps://www.bilibili.com/video/BV1Ae411C7xr/publicclassSemaphoreimplementsjava.io.Serializable同Reetrantlock在Sync继承AQSabstractstaticclassSyncextendsAbstractQueuedSynchronizer可以指定Sync是否是公平锁,默认非公平permits为设置AQS内stat......