java线程池

使用线程池解决问题

为任务创建新的线程并不一定不好,但是如果创建任务的频率高,而平均任务持续时间低,我们可以看到每项任务创建一个新的线程将产生性能(如果负载不可预知,还有稳定性)问题。

如果不是每项任务创建一个新的线程,则服务器应用程序必须采取一些方法来限制一次可以处理的请求数。这意味着每次需要启动新的任务时,它不能仅调用下列代码。

new Thread(runnable).start()

管理一大组小任务的标准机制是组合工作队列线程池。工作队列就是要处理的任务的队列,前面描述的 Queue 类完全适合。线程池是线程的集合,每个线程都提取公用工作队列。当一个工作线程完成任务处理后,它会返回队列,查看是否有其他任务需要处理。如果有,它会转移到下一个任务,并开始处理。

线程池为线程生命周期间接成本问题和资源崩溃问题提供了解决方案。通过对多个任务重新使用线程,创建线程的间接成本将分布到多个任务中。作为一种额外好处,因为请求到达时,线程已经存在,从而可以消除由创建线程引起的延迟。因此,可以立即处理请求,使应用程序更易响应。而且,通过正确调整线程池中的线程数,可以强制超出特定限制的任何请求等待,直到有线程可以处理它,它们等待时所消耗的资源要少于使用额外线程所消耗的资源,这样可以防止资源崩溃。

 

Executor 框架

java.util.concurrent 包中包含灵活的线程池实现,但是更重要的是,它包含用于管理实现 Runnable 的任务的执行的整个框架。该框架称为 Executor 框架。

Executor 接口相当简单。它描述将运行 Runnable 的对象:

public interface Executor { 
  void execute(Runnable command);
}

任务运行于哪个线程不是由该接口指定的,这取决于使用的 Executor 的实现。它可以运行于后台线程,如 Swing 事件线程,或者运行于线程池,或者调用线程,或者新的线程,它甚至可以运行于其他 JVM!通过同步的 Executor 接口提交任务,从任务执行策略中删除任务提交。Executor 接口独自关注任务提交 —— 这是 Executor 实现的选择,确定执行策略。这使在部署时调整执行策略(队列限制、池大小、优先级排列等等)更加容易,更改的代码最少。

java.util.concurrent 中的大多数 Executor 实现还实现 ExecutorService 接口,这是对 Executor 的扩展,它还管理执行服务的生命周期。这使它们更易于管理,并向生命可能比单独 Executor 的生命更长的应用程序提供服务。

public interface ExecutorService extends Executor {
  void shutdown();
  List<Runnable> shutdownNow();
  boolean isShutdown();
  boolean isTerminated();
  boolean awaitTermination(long timeout,
                           TimeUnit unit);
  // other convenience methods for submitting tasks
}

 

Executor

java.util.concurrent 包包含多个 Executor 实现,每个实现都实现不同的执行策略。什么是执行策略?执行策略定义何时在哪个线程中运行任务,执行任务可能消耗的资源级别(线程、内存等等),以及如果执行程序超载该怎么办。

执行程序通常通过工厂方法例示,而不是通过构造函数。Executors 类包含用于构造许多不同类型的 Executor 实现的静态工厂方法:

  • Executors.newCachedThreadPool() 创建不限制大小的线程池,但是当以前创建的线程可以使用时将重新使用那些线程。如果没有现有线程可用,将创建新的线程并将其添加到池中。使用不到 60 秒的线程将终止并从缓存中删除。
  • Executors.newFixedThreadPool(int n) 创建线程池,其重新使用在不受限制的队列之外运行的固定线程组。在关闭前,所有线程都会因为执行过程中的失败而终止,如果需要执行后续任务,将会有新的线程来代替这些线程。
  • Executors.newSingleThreadExecutor() 创建 Executor,其使用在不受限制的队列之外运行的单一工作线程,与 Swing 事件线程非常相似。保证顺序执行任务,在任何给定时间,不会有多个任务处于活动状态。

 

 

### Java线程池的使用与实现 Java线程池是一种高效的并发编程工具,用于管理线程生命周期并减少线程创建和销毁的开销[^1]。通过复用线程线程池可以显著提高系统的性能和资源利用率。 #### Java线程池的核心组件 Java线程池主要由以下核心组件构成: - **核心线程数(corePoolSize)**:线程池中保持的最小线程数,即使这些线程处于空闲状态。 - **最大线程数(maximumPoolSize)**:线程池中允许的最大线程数。 - **任务队列(workQueue)**:用于保存等待执行任务的队列。 - **线程工厂(ThreadFactory)**:用于创建新线程的对象。 - **拒绝策略(RejectedExecutionHandler)**:当线程池无法处理新任务时的处理策略。 #### 创建线程池的方式 在Java中,可以通过`ThreadPoolExecutor`类来手动创建线程池,或者使用`Executors`工具类提供的静态方法来快速创建不同类型的线程池。 ##### 使用`ThreadPoolExecutor`创建线程池 以下是一个自定义线程池的示例代码: ```java import java.util.concurrent.*; public class CustomThreadPool { public static void main(String[] args) { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 10, // 核心线程数 25, // 最大线程数 10L, // 空闲线程存活时间 TimeUnit.SECONDS, // 时间单位 new LinkedBlockingDeque<>(), // 任务队列 new CustomThreadFactory(), // 自定义线程工厂 new ThreadPoolExecutor.AbortPolicy() // 拒绝策略 ); // 提交任务到线程池 for (int i = 0; i < 30; i++) { threadPoolExecutor.execute(() -> { System.out.println(Thread.currentThread().getName() + " is running."); }); } // 关闭线程池 threadPoolExecutor.shutdown(); } } class CustomThreadFactory implements ThreadFactory { private final AtomicInteger counter = new AtomicInteger(1); @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setName("自定义线程-" + counter.getAndIncrement()); return thread; } } ``` ##### 使用`Executors`创建线程池 `Executors`工具类提供了多种便捷方法来创建不同类型的线程池: - `newFixedThreadPool(int nThreads)`:创建一个固定大小的线程池。 - `newCachedThreadPool()`:创建一个可根据需要调整大小的线程池。 - `newSingleThreadExecutor()`:创建一个单线程线程池。 - `newScheduledThreadPool(int corePoolSize)`:创建一个支持定时及周期性任务执行的线程池。 #### `execute`与`submit`的区别 `execute`和`submit`是提交任务到线程池的两种方法,它们的主要区别在于返回值和异常处理方式: - `execute(Runnable command)`:仅接受`Runnable`类型的任务,不返回结果,且不会抛出异常[^2]。 - `submit(Callable<T> task)`或`submit(Runnable task)`:可以接受`Runnable`或`Callable`类型的任务,返回一个`Future`对象,用于获取任务执行结果或检查任务状态[^2]。 #### 线程池的最佳实践 - **合理设置线程池大小**:根据硬件资源和任务特性设置合适的线程池大小,避免过载或资源浪费。 - **选择合适的任务队列**:根据任务的优先级和数量选择适当的队列类型,如`LinkedBlockingQueue`或`ArrayBlockingQueue`。 - **定义明确的拒绝策略**:根据业务需求选择合适的拒绝策略,如`AbortPolicy`、`CallerRunsPolicy`等。 - **及时关闭线程池**:在不再需要线程池时调用`shutdown()`方法,确保线程池中的资源被正确释放。 #### 示例代码:使用`submit`方法 以下是一个使用`submit`方法的示例代码: ```java import java.util.concurrent.*; public class SubmitExample { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newSingleThreadExecutor(); Future<Integer> future = executorService.submit(() -> { Thread.sleep(2000); return 42; }); System.out.println("任务正在执行..."); System.out.println("任务结果:" + future.get()); executorService.shutdown(); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值