线程池的执行原理

线程池(ThreadPool)是一种基于池化技术的多线程管理机制,用于减少在创建和销毁线程上所花的开销和资源消耗。线程池中的线程可以循环利用,用于执行多个任务。

线程池的工作原理

线程池主要解决两个问题:首先,通过重复利用一系列已经创建好的线程,减少线程创建和销毁的性能开销;其次,通过对任务队列和线程的调度,可以细致地控制最大并发数,从而优化系统资源的使用和系统稳定性。

线程池的工作流程大致如下:

  1. 任务提交:将执行任务提交到线程池。如果线程池中的线程少于核心线程数,则创建新的线程执行这个任务;如果线程数已达到核心线程数,任务会被加入到队列中等待执行。
  2. 任务排队:如果队列也已满,且当前线程数小于最大线程数,则创建新的线程来处理被提交的任务;如果线程数已达到最大值,则根据饱和策略处理无法执行的任务。
  3. 任务执行:线程从队列中取出任务并执行。
  4. 线程闲置:在执行完任务后,线程不会立即销毁,而是可用于执行其他任务。
  5. 线程销毁:如果线程超过一定时间(keepAliveTime)没有任务执行,且当前线程数大于核心线程数,则这个线程会被销毁,以节省资源。

Java 中的线程池

在 Java 中,线程池是通过 Executor 框架实现的,具体来说是通过 ExecutorService 接口及其实现类,如 ThreadPoolExecutorScheduledThreadPoolExecutor

下面是 ThreadPoolExecutor 的一部分源码,简化和注释以解释其核心部分:

public class ThreadPoolExecutor extends AbstractExecutorService {
    // 线程池的主要状态,包含在一个原子变量中
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY = (1 << COUNT_BITS) - 1;
    
    // 状态在高位,工作线程数在低位
    private static final int RUNNING = -1 << COUNT_BITS;
    private static final int SHUTDOWN = 0 << COUNT_BITS;
    // ... 省略其他状态
    
    // 线程工厂
    private final ThreadFactory threadFactory;
    // 阻塞队列
    private final BlockingQueue<Runnable> workQueue;
    // 超时时间
    private final long keepAliveTime;
    // 线程池关闭的处理
    private final RejectedExecutionHandler handler;
    // 锁
    private final ReentrantLock mainLock = new ReentrantLock();
    // ... 省略其他成员

    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
                              long keepAliveTime, TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        // ... 初始化线程池
    }
    
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        // 如果工作线程数小于核心线程数,则尝试创建新线程执行任务
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // 如果线程池正在运行且任务能加到队列中,则完成任务提交
        if (isRunning(c) && workQueue.offer(command)) {
            // 二次检查
            int recheck = ctl.get();
            if (!isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        // 如果无法提交任务,则尝试创建新线程执行任务,如果失败,则执行拒绝策略
        else if (!addWorker(command, false))
            reject(command);
    }
    
    // ... 省略其他方法
}

这段代码虽然没有展示 ThreadPoolExecutor 的全部复杂性,但它揭示了一些核心组成部分:

  • ctl:这是一个 AtomicInteger,它打包了线程池的状态和线程数量信息。
  • workQueue:一个阻塞队列,用于存储等待执行的任务。
  • keepAliveTime:非核心线程空闲时的存活时长。
  • threadFactory:用来创建新线程的工厂。
  • handler:线程池饱和时的策略接口。

示例代码

演示如何使用 ThreadPoolExecutor

import java.util.concurrent.*;

public class ThreadPoolExample {
    public static void main(String[] args) {
        // 创建线程池
        ExecutorService executor = new ThreadPoolExecutor(
            2, // 核心线程数
            4, // 最大线程数
            100, // 保持存活时间
            TimeUnit.SECONDS, // 时间单位
            new LinkedBlockingQueue<>(10), // 任务队列
            Executors.defaultThreadFactory(), // 线程工厂
            new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
        );

        // 提交任务
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            executor.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " is executing task " + finalI);
                // 模拟耗时操作
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        // 关闭线程池
        executor.shutdown();
    }
}

当你运行这个示例时,你会看到一个 ThreadPoolExecutor 是如何初始化和使用的。线程池首先尝试使用核心线程来执行任务,如果核心线程忙碌,则任务会被加入队列中。如果队列已满,线程池将创建新的线程(直到达到最大线程数)。如果线程数达到最大值并且队列也满了,新提交的任务将被拒绝处理。

线程池是并发编程中使用频率非常高的组件。了解其内部原理和适当的使用方法对于开发高效、稳定的并发应用至关重要。

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring中的线程池是通过ThreadPoolTaskExecutor类来实现的,该类最终调用了Java中的ThreadPoolExecutor类的一些方法来实现线程池的功能。线程池的工作原理如下: 1. 配置线程池大小:可以通过ThreadPoolTaskExecutor类的setCorePoolSize()方法和setMaxPoolSize()方法来配置线程池的核心线程数和最大线程数。核心线程数是线程池中保持活动状态的线程数,最大线程数是线程池中允许的最大线程数。 2. 提交任务:可以使用execute()方法向线程池提交任务。execute()方法是Executor接口中声明的方法,在ThreadPoolExecutor类中有具体的实现。该方法将任务提交给线程池,由线程池中的线执行。 3. 执行任务:线程池会根据配置的核心线程数和最大线程数来管理线程的数量。当有任务提交时,线程池会根据当前线程数和任务队列的情况来决定是创建新的线程还是将任务放入队列中等待执行。如果线程池中的线程数小于核心线程数,则会创建新的线程来执行任务。如果线程池中的线程数已经达到核心线程数,但任务队列未满,则会将任务放入队列中等待执行。如果线程池中的线程数已经达到核心线程数且任务队列已满,但线程池中的线程数小于最大线程数,则会创建新的线程来执行任务。如果线程池中的线程数已经达到最大线程数且任务队列已满,则会根据配置的拒绝策略来处理无法执行的任务。 4. 返回结果:execute()方法没有返回值,而submit()方法有返回值。可以使用submit()方法向线程池提交任务,并获取任务的执行结果。 总结起来,Spring的线程池工作原理是通过ThreadPoolTaskExecutor类来实现的,该类调用了Java中的ThreadPoolExecutor类的一些方法来管理线程池的大小和执行任务。通过配置线程池的核心线程数、最大线程数和任务队列,线程池可以根据任务的提交情况来动态地管理线程的数量,并通过拒绝策略来处理无法执行的任务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辞暮尔尔-烟火年年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值