java线程池概述

Java线程池的实现主要通过`java.util.concurrent.ThreadPoolExecutor`类来完成,它是Java并发包中的核心类之一,提供了强大的线程池管理能力。下面通过分析`ThreadPoolExecutor`的部分关键源代码来详细说明其工作原理和使用方法。

### 创建线程池

首先,我们来看如何创建一个线程池。以下是一个简单的示例,展示了如何使用`ThreadPoolExecutor`的构造函数来创建一个线程池:```java

import java.util.concurrent.*;

public class ThreadPoolExample {
    public static void main(String[] args) {
        // 核心线程数
        int corePoolSize = 5;
        // 最大线程数
        int maximumPoolSize = 10;
        // 空闲线程存活时间
        long keepAliveTime = 60L;
        // 时间单位
        TimeUnit unit = TimeUnit.SECONDS;
        // 任务队列
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100);
        // 线程工厂,用于创建新线程
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        // 拒绝策略
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();

        // 创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler
        );
        
        // 提交任务到线程池
        for (int i = 0; i < 20; i++) {
            executor.execute(new RunnableTask());
        }
        
        // 关闭线程池
        executor.shutdown();
    }
    
    static class RunnableTask implements Runnable {
        @Override
        public void run() {
            System.out.println("Task executed by " + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}


```

### `ThreadPoolExecutor`关键属性和构造函数参数

- `corePoolSize`:线程池的核心线程数,即使线程空闲,除非设置了`allowCoreThreadTimeOut`为true,否则这部分线程也会一直存活。
- `maximumPoolSize`:线程池能够容纳的最大线程数。
- `keepAliveTime`:非核心线程闲置时的超时时长,超过这个时间会终止线程。
- `unit`:用于时间量度的单位,如`TimeUnit.SECONDS`。
- `workQueue`:任务队列,用于保存等待执行的任务,常用的有`LinkedBlockingQueue`、`ArrayBlockingQueue`等。
- `threadFactory`:用于创建新线程的工厂。
- `handler`:拒绝策略,当线程池和任务队列都满时,用于处理新的任务请求,常见的有`AbortPolicy`(抛出异常)、`CallerRunsPolicy`(调用者线程执行任务)、`DiscardPolicy`(丢弃任务)、`DiscardOldestPolicy`(丢弃队列最旧的任务并尝试重新提交当前任务)。

### 核心方法

- `execute(Runnable command)`:提交一个不需要返回值的任务执行,这是线程池最核心的方法。
- `submit(Callable<T> task)`:提交一个有返回值的任务执行,并返回一个Future对象用于获取结果。
- `shutdown()`:启动有序关闭,停止接收新任务,但已提交的任务会继续执行直到完成。
- `shutdownNow()`:尝试停止所有正在执行的任务,并停止处理新任务,返回等待执行的任务列表。

### 源码解析简述

`ThreadPoolExecutor`内部维护了一个原子整数`ctl`,它用高3位表示线程池的状态(RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED),低29位表示当前活动线程数。通过`ctl`的CAS操作来管理线程池状态和线程数量,保证了线程安全。

在`execute`方法中,线程池会根据当前的线程数和任务队列情况,决定是创建新线程、加入任务队列还是拒绝任务。如果需要创建新线程,它会通过`threadFactory`创建,并将其封装成`Worker`对象,然后启动这个线程去执行任务队列中的任务。

以上是`ThreadPoolExecutor`的基础使用和部分源码原理的简要介绍,实际上线程池的实现远比此复杂,还包括了工作窃取、线程复用、线程中断、任务拒绝处理等多种高级特性。

 

### 线程池的工作流程

了解线程池的工作流程对于深入理解其机制非常重要。下面是`ThreadPoolExecutor`执行任务的一个简化流程:

1. **任务提交**:当通过`execute()`方法提交一个任务时,线程池会执行以下判断逻辑:
   - 如果当前运行的线程数少于核心线程数(`corePoolSize`),即使线程池中有空闲线程,也会直接创建一个新线程来执行任务。
   - 如果当前运行的线程数等于核心线程数,任务会被放入任务队列中排队等待执行。
   - 如果任务队列已满,并且当前运行的线程数小于最大线程数(`maximumPoolSize`),则会创建新的线程来执行任务。
   - 如果以上条件都不满足,即线程数达到最大并且任务队列也满了,此时会触发拒绝策略处理这个任务。

2. **线程复用**:一旦线程创建,除非线程池关闭或者线程被允许超时(通过`allowCoreThreadTimeOut(true)`配置),否则线程会一直存活,等待执行新的任务。这意味着任务执行完毕后,线程并不会立即销毁,而是进入空闲状态,等待下一个任务到来。

3. **线程回收**:对于超过核心线程数的线程(即非核心线程),在空闲时间超过`keepAliveTime`后,会自动终止。核心线程是否允许超时取决于构造函数的配置。

4. **任务队列**:线程池中的任务队列是线程池的重要组成部分,它决定了任务的排队策略。常见的队列有无界队列(如`LinkedBlockingQueue`,可能导致任务无限堆积)、有界队列(如`ArrayBlockingQueue`,限制了任务队列的大小,有助于防止资源耗尽)、同步队列(如`SynchronousQueue`,不存储任务,直接将任务交给线程执行,适合大量短任务快速处理的场景)。

5. **拒绝策略**:当线程池和任务队列都达到了它们的容量上限,新提交的任务将会由拒绝策略处理。Java提供了几种内置的拒绝策略:
   - `AbortPolicy`:默认策略,抛出`RejectedExecutionException`异常。
   - `CallerRunsPolicy`:调用者所在线程自己去执行该任务。
   - `DiscardPolicy`:直接丢弃任务,不执行也不抛出异常。
   - `DiscardOldestPolicy`:丢弃队列中最旧的任务,然后尝试重新提交被拒绝的任务。

### 扩展知识

- **线程池监控**:`ThreadPoolExecutor`提供了丰富的监控接口,如`getPoolSize()`、`getActiveCount()`、`getQueue()`等,可以帮助开发者监控线程池的运行状态。
- **自定义线程池**:虽然`Executors`类提供了便捷方法来创建不同类型的线程池,但为了更精确地控制线程池的行为,推荐直接使用`ThreadPoolExecutor`构造函数自定义线程池参数。
- **工作窃取(Work Stealing)**:Java的`ForkJoinPool`是一个特殊的线程池,实现了工作窃取算法,适用于大量相互独立的任务。在这种模型下,空闲的线程可以从其他繁忙线程的任务队列中“窃取”任务来执行,提高了多核处理器的利用率。

理解线程池的运作机制和合理配置线程池参数,对于提升程序的性能、响应时间和资源利用率至关重要。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值