线程池的原理

1、线程池简介:
    多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。    
    假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。

    如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能。
                一个线程池包括以下四个基本组成部分:
                1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
                2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
                3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
                4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
                
    线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。
    线程池不仅调整T1,T3产生的时间段,而且它还显著减少了创建线程的数目,看一个例子:
    假设一个服务器一天要处理50000个请求,并且每个请求需要一个单独的线程完成。在线程池中,线程数一般是固定的,所以产生线程总数不会超过线程池中线程的数目,而如果服务器不利用线程池来处理这些请求则线程总数为50000。一般线程池大小是远小于50000。所以利用线程池的服务器程序不会为了创建50000而在处理请求时浪费时间,从而提高效率。

    代码实现中并没有实现任务接口,而是把Runnable对象加入到线程池管理器(ThreadPool),然后剩下的事情就由线程池管理器(ThreadPool)来完成了

 

[java]  view plain copy
  1. package mine.util.thread;  
  2.   
  3. import java.util.LinkedList;  
  4. import java.util.List;  
  5.   
  6. /** 
  7.  * 线程池类,线程管理器:创建线程,执行任务,销毁线程,获取线程基本信息 
  8.  */  
  9. public final class ThreadPool {  
  10.     // 线程池中默认线程的个数为5  
  11.     private static int worker_num = 5;  
  12.     // 工作线程  
  13.     private WorkThread[] workThrads;  
  14.     // 未处理的任务  
  15.     private static volatile int finished_task = 0;  
  16.     // 任务队列,作为一个缓冲,List线程不安全  
  17.     private List<Runnable> taskQueue = new LinkedList<Runnable>();  
  18.     private static ThreadPool threadPool;  
  19.   
  20.     // 创建具有默认线程个数的线程池  
  21.     private ThreadPool() {  
  22.         this(5);  
  23.     }  
  24.   
  25.     // 创建线程池,worker_num为线程池中工作线程的个数  
  26.     private ThreadPool(int worker_num) {  
  27.         ThreadPool.worker_num = worker_num;  
  28.         workThrads = new WorkThread[worker_num];  
  29.         for (int i = 0; i < worker_num; i++) {  
  30.             workThrads[i] = new WorkThread();  
  31.             workThrads[i].start();// 开启线程池中的线程  
  32.         }  
  33.     }  
  34.   
  35.     // 单态模式,获得一个默认线程个数的线程池  
  36.     public static ThreadPool getThreadPool() {  
  37.         return getThreadPool(ThreadPool.worker_num);  
  38.     }  
  39.   
  40.     // 单态模式,获得一个指定线程个数的线程池,worker_num(>0)为线程池中工作线程的个数  
  41.     // worker_num<=0创建默认的工作线程个数  
  42.     public static ThreadPool getThreadPool(int worker_num1) {  
  43.         if (worker_num1 <= 0)  
  44.             worker_num1 = ThreadPool.worker_num;  
  45.         if (threadPool == null)  
  46.             threadPool = new ThreadPool(worker_num1);  
  47.         return threadPool;  
  48.     }  
  49.   
  50.     // 执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定  
  51.     public void execute(Runnable task) {  
  52.         synchronized (taskQueue) {  
  53.             taskQueue.add(task);  
  54.             taskQueue.notify();  
  55.         }  
  56.     }  
  57.   
  58.     // 批量执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定  
  59.     public void execute(Runnable[] task) {  
  60.         synchronized (taskQueue) {  
  61.             for (Runnable t : task)  
  62.                 taskQueue.add(t);  
  63.             taskQueue.notify();  
  64.         }  
  65.     }  
  66.   
  67.     // 批量执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定  
  68.     public void execute(List<Runnable> task) {  
  69.         synchronized (taskQueue) {  
  70.             for (Runnable t : task)  
  71.                 taskQueue.add(t);  
  72.             taskQueue.notify();  
  73.         }  
  74.     }  
  75.   
  76.     // 销毁线程池,该方法保证在所有任务都完成的情况下才销毁所有线程,否则等待任务完成才销毁  
  77.     public void destroy() {  
  78.         while (!taskQueue.isEmpty()) {// 如果还有任务没执行完成,就先睡会吧  
  79.             try {  
  80.                 Thread.sleep(10);  
  81.             } catch (InterruptedException e) {  
  82.                 e.printStackTrace();  
  83.             }  
  84.         }  
  85.         // 工作线程停止工作,且置为null  
  86.         for (int i = 0; i < worker_num; i++) {  
  87.             workThrads[i].stopWorker();  
  88.             workThrads[i] = null;  
  89.         }  
  90.         threadPool=null;  
  91.         taskQueue.clear();// 清空任务队列  
  92.     }  
  93.   
  94.     // 返回工作线程的个数  
  95.     public int getWorkThreadNumber() {  
  96.         return worker_num;  
  97.     }  
  98.   
  99.     // 返回已完成任务的个数,这里的已完成是只出了任务队列的任务个数,可能该任务并没有实际执行完成  
  100.     public int getFinishedTasknumber() {  
  101.         return finished_task;  
  102.     }  
  103.   
  104.     // 返回任务队列的长度,即还没处理的任务个数  
  105.     public int getWaitTasknumber() {  
  106.         return taskQueue.size();  
  107.     }  
  108.   
  109.     // 覆盖toString方法,返回线程池信息:工作线程个数和已完成任务个数  
  110.     @Override  
  111.     public String toString() {  
  112.         return "WorkThread number:" + worker_num + "  finished task number:"  
  113.                 + finished_task + "  wait task number:" + getWaitTasknumber();  
  114.     }  
  115.   
  116.     /** 
  117.      * 内部类,工作线程 
  118.      */  
  119.     private class WorkThread extends Thread {  
  120.         // 该工作线程是否有效,用于结束该工作线程  
  121.         private boolean isRunning = true;  
  122.   
  123.         /* 
  124.          * 关键所在啊,如果任务队列不空,则取出任务执行,若任务队列空,则等待 
  125.          */  
  126.         @Override  
  127.         public void run() {  
  128.             Runnable r = null;  
  129.             while (isRunning) {// 注意,若线程无效则自然结束run方法,该线程就没用了  
  130.                 synchronized (taskQueue) {  
  131.                     while (isRunning && taskQueue.isEmpty()) {// 队列为空  
  132.                         try {  
  133.                             taskQueue.wait(20);  
  134.                         } catch (InterruptedException e) {  
  135.                             e.printStackTrace();  
  136.                         }  
  137.                     }  
  138.                     if (!taskQueue.isEmpty())  
  139.                         r = taskQueue.remove(0);// 取出任务  
  140.                 }  
  141.                 if (r != null) {  
  142.                     r.run();// 执行任务  
  143.                 }  
  144.                 finished_task++;  
  145.                 r = null;  
  146.             }  
  147.         }  
  148.   
  149.         // 停止工作,让该线程自然执行完run方法,自然结束  
  150.         public void stopWorker() {  
  151.             isRunning = false;  
  152.         }  
  153.     }  
  154. }  

 

测试代码:

[java]  view plain copy
  1. package mine.util.thread;  
  2.   
  3. //测试线程池  
  4. public class TestThreadPool {  
  5.     public static void main(String[] args) {  
  6.         // 创建3个线程的线程池  
  7.         ThreadPool t = ThreadPool.getThreadPool(3);  
  8.         t.execute(new Runnable[] { new Task(), new Task(), new Task() });  
  9.         t.execute(new Runnable[] { new Task(), new Task(), new Task() });  
  10.         System.out.println(t);  
  11.         t.destroy();// 所有线程都执行完成才destory  
  12.         System.out.println(t);  
  13.     }  
  14.   
  15.     // 任务类  
  16.     static class Task implements Runnable {  
  17.         private static volatile int i = 1;  
  18.   
  19.         @Override  
  20.         public void run() {// 执行任务  
  21.             System.out.println("任务 " + (i++) + " 完成");  
  22.         }  
  23.     }  
  24. }  


 

运行结果:

WorkThread number:3  finished task number:0  wait task number:6
任务 1 完成
任务 2 完成
任务 3 完成
任务 4 完成
任务 5 完成
任务 6 完成
WorkThread number:3  finished task number:6  wait task number:0

分析:由于并没有任务接口,传入的可以是自定义的任何任务,所以线程池并不能准确的判断该任务是否真正的已经完成(真正完成该任务是这个任务的run方法执行完毕),只能知道该任务已经出了任务队列,正在执行或者已经完成。

2、java类库中提供的线程池简介:

     java提供的线程池更加强大,相信理解线程池的工作原理,看类库中的线程池就不会感到陌生了。

<think>嗯,用户之前让我介绍了深度学习的几个主要模型,现在他们问的是线程池原理和应用。我需要先确定用户的知识背景。之前他们可能对深度学习有一定的了解,但现在转向了多线程编程,可能是计算机科学的学生或者开发者,想优化程序性能。 线程池原理,首先得解释线程池是什么,为什么需要它。用户可能知道多线程的基本概念,但线程池的具体机制可能不太清楚。应该从线程池的组成结构开始讲,比如任务队列、工作线程、线程管理器这些部分。还要提到线程池如何减少线程创建和销毁的开销,提高响应速度,控制并发数量。 接下来是工作原理,用户需要知道线程池的工作流程,比如初始化线程,任务提交到队列,线程从队列中取出任务执行,任务完成后线程不销毁而是等待下一个任务。这部分需要详细但简洁,可能需要用步骤分点说明。 然后是核心参数,最大线程数、核心线程数、队列容量这些参数的作用和如何影响性能。用户可能在实际应用中需要调整这些参数,所以解释清楚每个参数的意义和配置策略很重要。 应用场景方面,用户可能想知道在什么情况下使用线程池。比如高并发请求的Web服务器,数据库连接池,批量数据处理,定时任务调度等。这些例子能帮助用户将理论应用到实际项目中。 优势部分需要强调资源复用、流量削峰、可控性这些好处,可能用户关心如何提升系统性能,避免资源耗尽的问题。还需要提到可能的风险,比如任务队列过大导致内存溢出,或者死锁问题,这样用户在实际使用时会注意这些陷阱。 最后,实现示例部分,用户可能需要一个简单的代码示例来理解线程池的使用。不过要注意,用户可能使用不同的编程语言,但线程池的概念是通用的,所以用伪代码或者Java的示例比较合适,因为Java的线程池比较典型。不过用户可能也熟悉其他语言,所以可以提到不同语言的实现,但主要用Java示例。 在思考过程中,我需要确保回答结构清晰,按照原理、组成、工作流程、参数、应用场景、优势和风险的顺序来组织内容。同时,要使用用户容易理解的语言,避免过于专业的术语,必要时用比喻帮助理解。比如,把线程池比作银行窗口,任务队列比作排队的人,这样用户更容易想象线程池的工作方式。 用户可能还关心如何选择合适的线程池参数,这部分需要给出一些经验法则,比如根据CPU核心数来设置线程数量,或者根据任务类型调整队列大小。同时,要提醒用户不同场景下参数的不同配置,比如IO密集型任务和CPU密集型任务的区别。 另外,需要提到线程池在实际开发中的常见实现,比如Java中的Executor框架,或者Python的concurrent.futures模块,这样用户知道如何在自己的项目中应用线程池,而无需从头造轮子。 最后,总结线程池的重要性和应用价值,帮助用户形成整体的理解,确保他们能够将线程池应用到自己的项目中,优化程序性能,提高系统稳定性。</think>以下是对线程池原理与应用的系统性解析,采用分模块结构化说明: #### 一、线程池核心原理 1. **设计目标** 通过复用线程资源,解决频繁创建/销毁线程的性能损耗问题,数学模型可表示为: $$T_{total} = N \cdot (t_{create} + t_{destroy}) + t_{task} \quad \Rightarrow \quad T_{pool} = t_{create} + t_{destroy} + k \cdot t_{task}$$ 其中$N$为任务总数,$k$为线程池线程数。 2. **组件构成** - **任务队列**:缓冲待执行任务(FIFO/LIFO优先级队列) - **工作线程集合**:固定/动态数量的Thread对象 - **线程管理器**:负责线程创建/回收/状态监控 3. **工作流程** ```plaintext [任务提交] → [队列未满?存入队列] → [有空闲线程?立即执行] ↓队列已满 ↓无空闲线程 [创建新线程?] ←─ [根据拒绝策略处理] ``` #### 二、关键参数配置 | 参数 | 作用说明 | 典型设置规则 | |-------------------|----------------------------|-----------------------------| | 核心线程数(corePoolSize) | 常驻线程数量 | CPU密集型:核数+1 | | 最大线程数(maxPoolSize) | 应急线程上限 | IO密集型:2*核数/(1-阻塞系数) | | 队列容量(queueCapacity) | 缓冲任务容量 | 根据内存和吞吐量需求设定 | | 存活时间(keepAliveTime) | 非核心线程空闲回收时间 | 通常设为60-300秒 | #### 三、应用场景示例 1. **Web服务器** Tomcat默认线程池配置: ```java // maxThreads=200, acceptCount=100 Executor executor = new ThreadPoolExecutor( 10, 200, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100)); ``` 可承载QPS= (200线程)/(平均响应时间) 2. **数据库连接池** HikariCP采用等待队列优化: ```python # 设置最大连接数=10,最小空闲=2 pool = QueuePool(creator, max_overflow=0, pool_size=10) ``` 3. **批量数据处理** Spark任务调度优化案例: ```scala val pool = new ForkJoinPool( Runtime.getRuntime.availableProcessors() * 2) ``` #### 四、线程池实现模式 1. **Work-Stealing算法** Java的ForkJoinPool实现原理: $$P_{steal} = \frac{1}{n} \sum_{i=1}^{n} (Q_i.size() > \theta)$$ 当某个线程任务队列长度超过阈值θ时触发任务窃取 2. **拒绝策略对比** | 策略类型 | 行为特征 | 适用场景 | |----------------|----------------------------|----------------------| | AbortPolicy | 直接抛出RejectedException | 严格要求系统稳定性 | | CallerRunsPolicy| 主线程执行任务 | 流量削峰 | | DiscardPolicy | 静默丢弃任务 | 可容忍数据丢失 | #### 五、性能优化实践 1. **线程数计算公式** 根据任务类型动态调整: - **CPU密集型**:$N_{threads} = N_{cpu} + 1$ - **IO密集型**:$N_{threads} = N_{cpu} \times \frac{1 + \text{等待时间}}{\text{计算时间}}$ 2. **监控指标** ```java ThreadPoolExecutor pool = (ThreadPoolExecutor) executor; System.out.println("活跃线程: " + pool.getActiveCount()); System.out.println("队列堆积: " + pool.getQueue().size()); ``` #### 六、典型问题解决方案 1. **死锁预防** 避免任务间循环依赖,设置任务超时: ```java Future<?> future = executor.submit(task); future.get(5, TimeUnit.SECONDS); // 超时控制 ``` 2. **内存泄漏处理** 使用ThreadLocal后必须执行remove()操作: ```java try { threadLocal.set(data); // ...业务逻辑 } finally { threadLocal.remove(); } ``` #### 七、新型线程池技术 1. **虚拟线程(协程)** Java 19+的虚拟线程实现: ```java ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); ``` 2. **异步编排框架** CompletableFuture链式调用示例: ```java CompletableFuture.supplyAsync(() -> fetchData(), pool) .thenApplyAsync(data -> process(data), pool) .exceptionally(ex -> handleError(ex)); ``` 实际生产环境中,建议通过APM工具(如SkyWalking)监控线程池运行状态,结合具体业务场景持续优化参数配置。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值