接上文线程池原理(1) 线程池的创建 通过ThreadPoolExecutor构造函数实现(推荐) ? 线程池执行流程 任务缓冲 任务缓冲模块是线程池能够管理任务的核心部分。线程池的本质是对任务和线程的管理,而做到这一点最关键的思想就是将任务和线程两者解耦,不让两者直接关联,才可以做后续的分配工作。 线程池大小确定 线程池数量的确定一直是困扰着程序员的一个难题,大部分程序员在设定线程池大小的时候就是随心而定。 很多人甚至可能都会觉得把线程池配置过大一点比较好!我觉得这明显是有问题的。 I/O 密集型任务(2N):这种任务应用起来,系统会用大部分的时间来处理 I/O 交互,而线程在处理 I/O 的时间段内不会占用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用。 因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是 2N。 如何判断是 CPU 密集任务还是 IO 密集任务?
废话不多说,开始我们的线程池源码的第二轮阅读。 回顾 简单回顾下上一篇线程池源码中涉及的两个方法,一个是execute() 执行任务的入口,还有一个是addWorker() 最通俗地理解就是是否需要添加新线程。 开始是一个循环,要么执行worker自带的第一个任务(firstTask),要么通过getTask() 获取任务 有任务首先得保证线程池是正常的,以下两种情况均调用wt.interrupt() 给「线程设置中断标志位 」 线程池处于STOP状态,也就是不接受新任务,也不执行队列中的任务 如果线程的标志位已经为true,那么清楚标志位,此时的线程池状态为STOP状态,这里看起来可能比较别扭,有了第一种情况为什么还要第二种 一进来也是一个死循环,可以先聚焦「什么时候会退出循环」,肯定是「不正常的情况」下会退出 当线程池状态不处于RUNNING或者SHUTDOWN的时候,或者是当线程处于SHUTDOWN但是工作队列中没有任务
前言 线程池实现原理-1 addWorker实现 在看addWorker方法之前,我们先看一个例子,了解一下retry的使用 break retry 跳到retry处,且不再进入循环 continue = null || workQueue.isEmpty) * 1.如果当前线程池的状态>SHUTDOWN,addWorker返回false,添加任务失败 * 2.如果当前线程池的状态 workerStarted) addWorkerFailed(w); } return workerStarted; } 仔细理解一下这段代码,其实就能理解,当线程池处于 // 1.核心线程允许被销毁 // 2.核心线程数 > corePoolSize boolean timed = allowCoreThreadTimeOut || wc 2.wc > maximumPoolSize肯定要删除线程了 // 3.workQueue为空可以销毁线程,此时有可能所有线程都被销毁了 // 4.workQueue不为空
2 线程池的创建 Executors中提供了一系列静态方法创建线程池: newSingleThreadExecutor:一个单线程的线程池。如果因异常结束,会再创建一个新的,保证按照提交顺序执行。 通过 ctl.get() 得到线程池的当前线程数,如果线程数小于corePoolSize,则调用 **addWorker(commond,true)** 方法创建新的线程执行任务,否则执行步骤2; 2. 且条件2不满足,则返回false * 4.条件2解读:线程池为shutdown状态时且任务队列不为空时,可以新增空任务的线程来处理队列中的任务 */ SHUTDOWN、STOP、TERMINATED状态 * 条件2:线程池STOP、TERMINATED状态或workQueue为空 * 条件1与条件2同时为true, 如果当前线程是突然终止的,调用addWorker()创建工作线程 2. 当前线程不是突然终止,但当前工作线程数量小于线程池需要维护的线程数量,则创建工作线程。
vs submit() execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否; submit()方法用于提交需要返回值的任务。 线程池会返回一个 Future 类型的对象,通过这个 Future 对象可以判断任务是否执行成功 ,并且可以通过 Future 的 get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成, ,线程池的状态变为 SHUTDOWN。 线程池不再接受新任务了,但是队列里的任务得执行完毕。 shutdownNow() :关闭线程池,线程的状态变为 STOP。 线程池会终止当前正在运行的任务,并停止处理排队的任务并返回正在等待执行的 List。
主线程: 相当于生产者,只管向线程池提交任务。并不关心线程池是如何执行任务的。因此,并不关心是哪一个线程执行的这个任务。 线程池: 相当于消费者,负责接收任务,并将任务分配到一个空闲的线程中去执行。 python内置进程池 ? >>>执行结果 ? # 必须要有一个 main 测试 >>> if __name__ == "__main__": # Pool 的实例化必须在 main 测试之下 >>>pool = Pool(2) 池的其他操作 操作一 操作二: terminate - 中止进程池,中止所有任务。 # 会阻塞,知道结果产生了 >>> result = a_result.get() 使用线程池来实现并发服务器 ? >>>客户端 ? >>>执行结果 ?
位表示工作线程数,最左边3位表示线程池状态。 ); } 第2处 发生拒绝的原因有两个(1)线程池状态非Runing (2)等待队列已满。 /** * 根据当前线程池状态,检查是否可以添加新的任务线程,如果可以则创建并启动任务 * 如果一切正常则返回true。 返回false 的可能如下: * 1.线程池没有处于RUNNING状态 * 2.线程工程创建新的任务线程失败 * @param firstTask 外部启动线程池时需要构造的第一个线程 corePoolSize : maximumPoolSize)) // 最大线程数不能超过2^29,否则影响左边3位的线程池状态值 return false;
所以需要通过线程池协调多个线程,并实现类似主次线程隔离、定时执行、周期执行等任务。线程池的作用包括: 利用线程池管理并复用线程、控制最大并发数等。 实现任务线程队列缓存策略和拒绝机制。 在了解线程池的基本作用后,我们学习一下线程池是如何创建线程的。 这个值的设置非常关键,设置过大会浪费资源,设置的过小会导致线程频繁地创建或销毁。 第2个参数:maximumPoolSize 表示线程池能够容纳同时执行的最大线程数。 从代码第2处来看,队列、线程工程、拒绝处理服务都必须有实例对象,但在实际编码中,很少有程序员对着三者进行实例化,而通过Executors这个线程池静态工厂提供默认实现,那么Executors与ThreadPoolExecutor execute(task1); } } } 当任务被拒绝的时候,拒绝策略会打印出当前线程池的大小以及达到了maximumPoolSize=2 ,且队列已满。
文章目录 一、线程池作用 二、线程池种类 三、线程池工作机制 四、线程池任务调度源码解析 一、线程池作用 ---- 线程池作用 : ① 避免创建线程 : 避免每次使用线程时 , 都需要 创建线程对象 ; ---- 线程池种类 : ① newCachedThreadPool : 可缓存线程池 , 如果 线程池线程个数已满 , 回收空闲线程 , 如果没有空闲线程 , 此时会创建新线程 ; ② newFixedThreadPool 后到的后执行 ) , LIFO 后入先出 ( 后到的先执行 ) ; 三、线程池工作机制 ---- 线程池线程相关概念: 线程数 : 线程池的 有 最大线程数 MaxSzie , 核心线程数 CoreSize , 任务拒绝后 , 处理善后 ; 四、线程池任务调度源码解析 ---- 在 AsyncTask.java 中 , 在静态代码块中 , 自己 自定义创建了线程池 , 没有使用上述四种线程池 ; 创建线程池时传入的参数 * 调用 addWorker 方法会检查运行状态, 和线程运行个数, 避免在不应该添加线程时执行错误操作. * * 2.
文章目录 一、线程池简介 二、线程池初始化方法简介 三、线程池使用示例 一、线程池简介 ---- 线程池一般是实现了 ExecutorService 接口的类 , 一般使用 ThreadPoolExecutor , 合理控制并发数 , 能提高 CPU 使用效率 ; 二、线程池初始化方法简介 ---- 线程池初始化方法简介 : newCachedThreadPool : 创建 可缓存线程池 ; 如果线程池长度超过处理需要 newScheduledThreadPool : 创建 定长周期任务线程池 ; 该线程池支持周期性任务执行 ; newSingleThreadExecutor : 创建 单线程化线程池 ; 该线程只有一个工作线程 是 自己配置的线程池 , 没有使用 Java 默认提供的四种线程池 , Java 提供的四种线程池是 可缓存线程池 , 定长线程池 , 定长周期任务线程池 , 单线程线程池 ; THREAD_POOL_EXECUTOR : 线程池线程分类 : 线程池的线程分为 核心线程 , 非核心线程 两类 ; 非核心线程闲置时间 : 非核心线程 超过一定的闲置时间 , 就会被回收 ; 假设线程池最大线程数是 8 , 核心线程数
concurrent.futures --- 启动并行任务 — Python 3.7.13 文档concurrent.futures 模块提供异步执行可调用对象高层接口异步执行可以由 ThreadPoolExecutor 使用线程或由 **Executor**ThreadPoolExecutor 线程池```pythonimport concurrent.futuresimport urllib.requestURLS = ['http exc)) else: print('%r page is %d bytes' % (url, len(data)))```ProcessPoolExecutor 进程池使用进程池来实现异步执行调用 112272535095293, 115280095190773, 115797848077099, 1099726899285419]def is_prime(n): if n % 2 = 0: return False sqrt_n = int(math.floor(math.sqrt(n))) for i in range(3, sqrt_n + 1, 2)
int corePoolSize = 2; /* 核心线程池的最大线程数 */ int maxPoolSize = 4; /* 线程最大空闲时间 */ 不推荐使用Executors的静态方法创建线程池 !!! 第2节 ForkJoinPool ---- ForkJoin线程池处理无返回值任务。 初始化数组用时:1847192纳秒, 初始化数组总和:493016 线程池计算用时:4220889纳秒, 线程池执行结果:493016 ? 第3节 两种线程池的比较 ---- ThreadPoolExecutor——适用于IO密集型任务 1.HTTP 2.RPC 3.DB 4.Redis 5.MQ 6.ZK ForkJoinPool——
像这种,提前创建好线程,需要的时候直接使用,我们称之为线程池。这种本质上就是一个生产消费模型。 线程池实现 //ThreadPool.hpp #pragma once #include<iostream> #include<unistd.h> #include<string> #include< tm_sec); return std::string(buffer); } #define SCREEN_TYPE 1 #define FILE_TYPE 2 lg.Enable(SCREEN_TYPE);}while(0) #define EnableFile() do{lg.Enable(FILE_TYPE);}while(0) }; 携带日志的线程池设计 Task>(); tp->Init(); tp->Start(); int cnt=10; while (cnt) { // 不断地向线程池推送任务
线程池 作用: 增加了线程的复用,降低了系统的开销 原理: 每当一个新的任务要执行的时候,系统会创建一个新的线程去执行任务,直到池中的线程数达到了设置的核心线程数,此时当新的任务要执行的时候,如果线程池中有空闲的线程 如果无法将任务加入队列(比如使用的是有界队列),则创建新的线程,如果此时线程数大于等于了线程池预设的最大线程数,那么任务将被拒绝。 java.util.concurrent包对线程池的支持: ExecutorService ThreadPoolExecutor (ExecutorService的默认实现类) 1、单线程的线程池实现 Executors.newSingleThreadExecutor 2、固定大小的线程池实现 Executors.newFixedThreadPool 3、可缓存的线程池实现 Executors.newCachedThreadPool 4、可定时执行任务的无大小限制的线程池实现 Executors.newScheduleThreadPool
* * 二、线程池的体系结构: * java.util.concurrent.Executor : 负责线程的使用与调度的根接口 * |--**ExecutorService 子接口: 线程池的主要接口 * ExecutorService newCachedThreadPool() : 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。 * ExecutorService newSingleThreadExecutor() : 创建单个线程池。 ExecutorService pool = Executors.newFixedThreadPool(5); ExecutorService pool2 = Executors.newSingleThreadExecutor (); ExecutorService pool3 = Executors.newCachedThreadPool(); //2 创建10个线程 执行线程 //结果: 每个线程都要按顺序
什么是线程池 把线程进行池化,调用的时候直接去池里面去取,而不是每次去重新创建,不用的时候放回线程池,而不是直接销毁,从而达到复用。 为什么使用线程池 ①复用线程,降低创建以及销毁导致的资源消耗。 ThreadPoolExecutor JDK提供的线程池 ThreadPoolExecutor的构造方法有七个参数 int corePoolSize核心线程数 int maximumPoolSize约定的线程最大数量 long keepAliveTime线程空闲的时候存活多久(但会保留核心线程数的线程数量) TimeUnit unit时间单位 BlockingQueue<Runnable>workQueue线程超过核心线程数的部分放到阻塞队列中 DiscardOldestPolicy最早放入的先丢弃 AbortPolicy直接抛出异常,也是默认的策略 CallerRunsPolicy谁提交的谁执行 DiscardPolicy直接丢弃 合理配置线程池 任务的特性有关 CPU密集型 (大量计算型任务)不要超过机器上CPU同时运行的线程个数 IO密集型 (文件读写)2*CPU的个数(常见配置) 混合型 尽量进行拆分
为什么要用线程池? 降低资源消耗。通过重复利用已创建的线程降低创建和销毁造成的消耗; 提高响应速度。当任务到达时,无须等待线程创建完成就能立即执行任务; 提高线程的可管理性。 如果要让线程池执行任务,需要实现的 Runnable 接口或 Callable 接口。 如何创建线程池 《阿里巴巴Java开发手册》中,强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式更加明确线程池的运行规则,规避资源耗尽的风险 若有新的任务被提交到该线程池,则任务会被保存在一个任务队列中,待线程空闲时,按先入先出的顺序执行队列中的任务; CachedThreadPool:该方法返回一个可根据实际情况调整线程数量的线程池。 线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程;若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
Java 线程池的作用 现在服务器端的应用程序几乎都采用了“线程池”技术,这主要是为了提高系统效率。 线程池就是为了尽量减少这种情况的发生。 下面我们来看看怎么用Java实现一个线程池。一个比较简单的线程池至少应包含线程池管理器、工作线程、任务队列、任务接口等部分。 其中线程池管理器(ThreadPool Manager)的作用是创建、销毁并管理线程池,将工作线程放入线程池中;工作线程是一个可以循环执行任务的线程,在没有任务时进行等待;任务队列的作用是提供一种缓冲机制 另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。 2.线程池的组成部分 一个比较简单的线程池至少应包含线程池管理器、工作线程、任务列队、任务接口等部分。 线程池管理器至少有下列功能:创建线程池,销毁线程池,添加新任务。 工作线程是一个可以循环执行任务的线程,在没有任务时将等待。
来,随我吃透线程池!!! 线程池的作用 线程的创建和销毁的开销是非常大的,线程创建,直接依靠操作系统。 大量的线程的创建,会给操作系统和jvm虚拟机带来压力,同时,大量的销毁也会给垃圾回收器带来压力 所以,线程池的目的就是为了解决两个问题 1反复创建线程开销大 2过多的线程太多占用内存 线程池:通过少量线程的复用 适用线程池的场合 1服务器,服务器要收到大量请求,比如tomcat服务器,也是用线程池实现的 2开发中,5个以上的线程,就可用用线程池了 线程池的创建 核心参数配置说明 参数 说明 corePoolSize 2cacheThreadPool,这种就是可缓存线程,线程会自动回收 3ScheduleThreadPool.定期执行任务,定时的执行 4singleThreadThreadExcecutor,就是一个线程 "); } } 线程池实现源码 上面了解了下线程池的使用注意点,现在看下线程池怎么实现的 线程池的组成部分 线程池管理器:创建、管理线程池 工作线程:就是线程池中存在的线程 任务队列:这个就是参数里重要之一的工作队列
为什么要用线程池? 单线程方式存在以下几个问题: 线程的工作周期:假设线程创建所需时间为T1,线程执行任务所需时间为T2,线程销毁所需的时间为T3,往往是T1+T3大于T2,所以如果频繁的创建线程会损耗过多的额外时间。 线程池可以管理和控制线程,因为线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。 线程池提供队列,存放缓冲等待执行的任务。 1、任务优先向CorePool中提交,创建核心线程执行任务 2、在CorePool满了之后,任务被提交提交到任务队列,等待线程池空闲 3、在任务队列满了之后,但CorePool中还没有空闲线程,那么任务将被提交到 每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。