executor和task优先于线程。

本文介绍了Java 1.5引入的java.util.concurrent包中的Executor框架,这是一个基于接口的任务执行工具,提供了灵活的任务执行机制。文章详细讲解了ExecutorService的创建与使用方法,包括如何提交任务、优雅地关闭执行器等,并探讨了不同类型的线程池适用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在Java 1.5 发行版本中,Java平台中增加了java.util.concurrent。这个包中包含了Executor Framework,这是一个很灵活的基于接口的任务执行工具。它创建了一个在各方面都比工作队列更好,却只需要这一行代码:

ExecutorService executor = Executors.newSingleThreadExecutor();

下面是为执行提交一个runnable的方法:

executor.execute(runnable);

下面是告诉executor如何优雅的终止(如果做不到这一点,虚拟机可能将不会退出):

executor.shutdown()。

你可以利用executor service完成更多的事情。例如,可以等待完成一项特殊的任务(后台线程),你可以等待一个任务中任何任务或者所有任务完成(利用invokeAny或者invokeAll方法),你可以等待executor service优雅的完成终止(利用awaitTermination方法),你可以在任务完成时逐个地获取这些任务的结果(利用ExecutorCompletionService),等等。

如果想让不止一个线程来处理来自这个队列的请求,只要调用一个不同的静态工厂,这个工厂创建了一种不同的executor service,称作线程池。你可以用固定或者可变数目的线程创建一个线程池。java.util.concurrent.Executors类包含了静态工厂,能为你提供所需的大多数executor。然而,如果你想来点特别的,可以直接使用ThreadPoolExecutor类。这个类允许你控制线程池操作的几乎每个方面。

为特殊的应用程序选择executor service是很有技巧的。如果编写的是小程序,或者轻载的服务器,使用Executors.newCachedThreadPool通常是个不错的选择,因为它不需要配置,并且一般情况下能够正确的完成工作。但是对于大负载的服务器来说,缓存的线程池就不是很好的选择了!在缓存的线程池中,被提交的任务没有排成队列,而是直接交给线程执行。如果没有线程可用,就创建一个新的线程。如果服务器负载的太重,以致它所有的CPU都完全被占用了,当有更多的任务时,就会创建更多的线程,这样只会使情况变得更糟。因此,在大负载的产品服务器中,最好使用Executors.newFixedThreadPool,它为你提供了一个包含固定线程数目的线程池,或者为了最大限度的控制它,就直接使用ThreadPoolExecutor类。

你不仅应该尽量不要编写自己的工作队列,而且还应该尽量不直接使用线程。现在关键的抽象不再是Thread了,它以前可是既充当工作单元,又是执行机制。现在工作单元和执行机制是分开的。现在关键的抽象是工作单元,称作任务(task)。任务有两种:Runnable及其近亲Callable(它与Runnable域类似,但它会返回值)。执行任务的通用机制是executor service。如果从任务的角度来看问题,并让一个executor service替你执行任务,在选择适当地执行策略方面就获得了极大地灵活性。从本质上讲Executor Famework所做的工作是执行,犹如Collections Framework所做的工作是聚集(aggregation)一样。

Executor Framework也有一个可以代替java.util.Timer的东西,即ScheduledThreadPoolExecutor。虽然timer使用起来更加容易,但是被调度的线程池executor更加灵活。timer只用一个线程来执行任务,这在面对长期运行的任务时,会影响到定时的准确性。如果timer唯一的线程抛出未被捕获的异常,timer就会停止执行。被调度的线程池executor支持多个线程,并且优雅的从抛出未受检异常的任务中恢复。

### 进程、线程Task的概念及区别 #### 一、概念概述 1. **进程(Process)** - 进程是一个正在运行的程序实例,拥有独立的内存空间其他资源。它是系统分配资源的基本单位[^1]。 - 每个进程都有自己的地址空间、文件描述符集合以及信号处理机制。 2. **线程(Thread)** - 线程是操作系统能够进行运算调度的最小单位,也是CPU调度的基本单元[^2]。 - 它位于进程之内,作为其内部的一个执行分支。多个线程可以共享同一个进程的资源,如内存地址空间文件描述符[^3]。 - 线程不能单独存在,必须依附于某个进程。 3. **任务(Task)** - 在计算机科学中,“任务”通常指代需要完成的工作或操作序列。它可以由一个或多个线程来实现。 - 在某些编程框架(如.NET 或 Java 的 Executor Framework 中),`Task` 是一种高级抽象,表示异步工作的逻辑单元。它可能对应到具体的线程或者一组线程上,也可能不直接映射到底层硬件线程[^4]。 #### 二、主要区别 | 特性 | 进程 | 线程 | Task | |--------------|-------------------------------|------------------------------|---------------------------| | 资源消耗 | 较高 | 较低 | 非常低 | | 是否独立 | 可以完全独立 | 不可独立,依赖所属进程 | 抽象层次更高 | | 地址空间隔离 | 各自独立 | 共享父进程的地址空间 | 无固定地址空间 | | 创建开销 | 大 | 小 | 几乎忽略 | | 并发能力 | 单独进程间通信较复杂 | 易于实现多线程间的协作 | 提供更灵活的并发模型 | #### 三、关系分析 - **进程与线程的关系** 线程存在于进程中,属于某一特定进程的一部分;而不同进程之间相互隔离,彼此无法轻易访问对方的数据结构除非通过显式的IPC (Inter-process Communication)[^1]。 - **线程Task的关系** `Task` 常用来封装一段待执行的任务代码,在现代开发环境中往往基于线程池技术构建而成。这意味着当创建一个新的 task 时,并不一定立即启动新的 OS-level thread 来承载该作业——而是从预先存在的 worker threads 当中选取可用者加以利用[^5]。 - **进程与Task的关系** Tasks 属于应用层面的设计模式之一,它们并不严格绑定至任何具体的操作系统实体之上(比如 process/thread),因此可以说 tasks 更接近业务逻辑表达形式而非底层架构细节说明[^6]。 ```python import threading from concurrent.futures import ThreadPoolExecutor def example_task(): print(f"Running on {threading.current_thread().name}") with ThreadPoolExecutor(max_workers=5) as executor: futures = [executor.submit(example_task) for _ in range(10)] ``` 上述 Python 示例展示了如何借助线程池管理大量小型任务(Task),从而有效减少频繁创建销毁原生线程所带来的性能损耗问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值