[线程池] ------ 形象的描述线程池,用一个特好记的例子来记忆

线程池

为了减少线程频繁的创建和销毁过程,引入池的概念。
将一些线程先创建好放在线程池中,每次来任务就用池中的线程执行,空闲时池中线程就等待,但不销毁。

原始线程池的创建:
ThreadPoolExecutor executor1 = new ThreadPoolExecutor(入参);
入参有4种方式,分别对上面参数的赋值:threadFactory和handler不写就是用默认的
(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue workQueue);
(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory);
(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler);
(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);

线程池的主要参数

corePoolSize:核心线程池的大小,默认最开始线程池是没有线程的,只有当有任务来的时候会创建线程,然后这个线程以后就不会销毁了,会一直待在线程池中等待任务。
maximumPoolSize:最大线程数。表示线程池最多可以创建多少个线程,当任务队列放满后还有新任务时,会尝试将线程数提高到最大线程数。
keepAliveTime:非核心线程空闲销毁倒计时,即当线程池中的线程数,大于核心线程数时,针对多出来的线程(就叫非核心线程),如果空闲时间超过这个时间,就销毁。
unit:keepAliveTime参数的单位,可以是:

TimeUnit.DAYS;               //天
TimeUnit.HOURS;             //小时
TimeUnit.MINUTES;           //分钟
TimeUnit.SECONDS;           //秒
TimeUnit.MILLISECONDS;      //毫秒
TimeUnit.MICROSECONDS;      //微妙
TimeUnit.NANOSECONDS;       //纳秒

workQueue:阻塞队列,存放要等待的任务,也叫任务队列。可以是:

ArrayBlockingQueue
LinkedBlockingQueue
SynchronousQueue

threadFactory:线程工厂,用来创建线程。
handler:拒绝任务的策略,当线程数达到maximumPoolSize,并且workQueue也被任务放满时,再来就会被拒绝,可选的拒绝策略有:

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)。
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。

我们举个实际例子来记忆这些参数(重要)

例子的场景是火车票售票大厅,乘客买票的例子。
火车票售票大厅,往往会开多个窗口来卖票,我们把一个个乘客比作是一个个任务,把一个个售票窗口比作一个个线程

假设某县火车票售票大厅,第一天营业,刚上班,一个窗口也没开,这就是线程池的初始化
这时候来了第一位乘客a,同时唤醒第一个售票窗口A,开始办理业务。
此时马上又来了一个乘客b,窗口A正在被占用,于是又开启第二个窗口B为乘客b办理业务。
我们已知这个火车站就2个正式窗口,相当于corePoolSize=2

那么后面来的人就要开始等了,我们把售票大厅的凳子比作任务队列,比如只有3个凳子,即任务队列最多容纳3个任务
与此同时,前面的乘客ab都没办理完,所以窗口A和窗口B都被占用了,后面又连续来了3个乘客cde,刚好,cde都坐在了凳子上,那么凳子就被坐满了。

此时,fg二位乘客也跟着来了,那么此时2个正式窗口被占,3个凳子也被占,于是火车站管理员临时又加了两个窗口,
这两个临时窗口,就相当于非核心线程,前面那两个AB窗口,就是正式窗口,就是核心线程,总共最多开4个窗口,那么就有maximumPoolSize=4
注意了,这里是等到正式窗口被占完,凳子也被坐满,才开的临时窗口。即核心线程占完,队列填满,才会尝试使用非核心线程做任务。

此时7位乘客,算是把这个售票大厅的资源给用完了,后面又来了乘客h,他就进不去了,要么把他赶出去算是抛个异常,要么他就一直着尝试进去算是重试,等等拒绝他的策略,这就用到了拒绝策略

后来慢慢的,前面的乘客都把票买了,凳子也空了,临时窗口也空闲了,等了1个小时也没有任务了,管理员就把临时窗口又撤销了,只留两个正式窗口。这个1小时就是keepAliveTime=1,单位unit是小时TimeUnit.HOURS

常用的线程池使用方式:

常用的线程池,不用new ThreadPoolExecutor(入参)的方式创建,是用Executors来创建的。

可回收缓存线程池(corePoolSize = 0;maximumPoolSize = Integer.MAX_VALUE;线程空闲后都可以销毁,但要注意设置合适的回收时间)

Executors.newCachedThreadPool();
继续上面的火车站的例子,这个情况就相当于火车站没有正式窗口,全是临时窗口,有人就开临时窗口,没人就关了窗口。

固定线程池(corePoolSize = maximumPoolSize)

Executors.newFixedThreadPool(n);
这个情况相当于没有临时窗口了,但是凳子特别多,即workQueue.size() = Integer.MAX_VALUE ,正式窗口和凳子都满后,再来的人就直接拒绝策略给拒绝了。

单线程线程池(corePoolSize = maximumPoolSize = 1,串行用)

Executors.newSingleThreadExecutor();
这个情况就是只有一个正式窗口,其他的和固定线程池一样。

可调度线程池(支持定时与周期性任务)

Executors.newScheduledThreadPool(n);

实战例子

package com.zs.test;import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.concurrent.*;/*** @author zhangshuai**/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-config-test.xml")
public class TestThreadPool {Logger logger = LoggerFactory.getLogger(TestThreadPool.class);final static CountDownLatch countDownLatch = new CountDownLatch(14);private ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 6, 3, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(5));private ExecutorService executor2 = Executors.newCachedThreadPool();private ExecutorService executor3 = Executors.newSingleThreadExecutor();private ExecutorService executor4 = Executors.newFixedThreadPool(3);private ExecutorService executor5 = Executors.newScheduledThreadPool(2);@Testpublic void test() throws InterruptedException {for(int i=0;i<14;i++){MyTestTask myTestTask = new MyTestTask(i);executor.execute(myTestTask);logger.info("线程池中线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+executor.getQueue().size()+",已执行完别的任务数目:"+executor.getCompletedTaskCount());if(i == 6){//总共14个任务,核心线程数设置5个,最大线程数6个,// 我们让前7个任务执行完等待10秒,是为了让设置的销毁非核心线程的时间3秒起作用。//后面就能看到非核心线程的创建,销毁和再次创建。Thread.sleep(10000);}}executor.shutdown();countDownLatch.await();}class MyTestTask implements Runnable {private int taskNum;public MyTestTask(int num) {this.taskNum = num;}@Overridepublic void run() {try {logger.info("正在执行的taskNum: "+taskNum);try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}logger.info("task: "+taskNum+"执行完毕");} catch (Exception e) {e.printStackTrace();}finally{countDownLatch.countDown();}}}
}

执行完的结果是:

先创建5个线程执行5个任务01234
3秒后01234执行完
接着创建56任务
与此同时主线程执行sleep10秒
3秒后56任务执行完
当主线程醒来,再次执行后面的任务。

[pool-1-thread-1] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 0
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:1,队列中等待执行的任务数目:0,已执行完别的任务数目:0
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:2,队列中等待执行的任务数目:0,已执行完别的任务数目:0
[pool-1-thread-2] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 1
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:3,队列中等待执行的任务数目:0,已执行完别的任务数目:0
[pool-1-thread-3] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 2
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:4,队列中等待执行的任务数目:0,已执行完别的任务数目:0
[pool-1-thread-4] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 3
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:0,已执行完别的任务数目:0
[pool-1-thread-5] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 4
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完别的任务数目:0
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:2,已执行完别的任务数目:0[pool-1-thread-1] INFO  com.zs.test.TestThreadPool - task: 0执行完毕
[pool-1-thread-2] INFO  com.zs.test.TestThreadPool - task: 1执行完毕
[pool-1-thread-4] INFO  com.zs.test.TestThreadPool - task: 3执行完毕
[pool-1-thread-3] INFO  com.zs.test.TestThreadPool - task: 2执行完毕
[pool-1-thread-5] INFO  com.zs.test.TestThreadPool - task: 4执行完毕[pool-1-thread-1] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 5
[pool-1-thread-2] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 6
[pool-1-thread-1] INFO  com.zs.test.TestThreadPool - task: 5执行完毕
[pool-1-thread-2] INFO  com.zs.test.TestThreadPool - task: 6执行完毕[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完别的任务数目:7
[pool-1-thread-4] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 7
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完别的任务数目:7
[pool-1-thread-3] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 8
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完别的任务数目:7
[pool-1-thread-5] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 9
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完别的任务数目:7
[pool-1-thread-2] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 10
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完别的任务数目:7
[pool-1-thread-1] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 11
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完别的任务数目:7
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:2,已执行完别的任务数目:7
[pool-1-thread-4] INFO  com.zs.test.TestThreadPool - task: 7执行完毕
[pool-1-thread-3] INFO  com.zs.test.TestThreadPool - task: 8执行完毕
[pool-1-thread-5] INFO  com.zs.test.TestThreadPool - task: 9执行完毕
[pool-1-thread-2] INFO  com.zs.test.TestThreadPool - task: 10执行完毕
[pool-1-thread-1] INFO  com.zs.test.TestThreadPool - task: 11执行完毕
[pool-1-thread-5] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 12
[pool-1-thread-4] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 13
[pool-1-thread-5] INFO  com.zs.test.TestThreadPool - task: 12执行完毕
[pool-1-thread-4] INFO  com.zs.test.TestThreadPool - task: 13执行完毕

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499909.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分组密码的工作模式

一、理论基础1.概述密码学中&#xff0c;块密码的工作模式允许使用同一个块密码密钥对多于一块的数据进行加密&#xff0c;并保证其安全性。块密码自身只能加密长度等于密码块长度的单块数据&#xff0c;若要加密变长数据&#xff0c;则数据必须先被划分为一些单独的密码块。通…

数据仓库基本认知

数据仓库概念&#xff1a; 数据仓库&#xff0c;英文名称Data Warehouse&#xff0c;简写为DW。 是一种面向分析的存储系统。 他是一个很大的数据存储集合&#xff0c;出于企业的分析性报告和决策支持目的而创建&#xff0c;对多样的业务数据进行筛选与整合。 它为企业提供一…

PBOC3.0中使用的国密SM2算法

一、知识准备 PBOC3.0规范就是《中国金融集成电路&#xff08;IC&#xff09;卡规范》3.0版本。SM2是国密局推出的一种他们自己说具有自主知识产权的非对称商用密码算法。本身是基于ECC椭圆曲线算法的&#xff0c;所以要讲SM2, 先要弄懂ECC。 完全理解ECC算法需要一定的数学功底…

mapper注入失败,NoSuchBeanDefinitionException: No qualifying bean of type [com.xxx.XxxMapper] found for d

mapper注入失败: Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.xxx.XxxMapper] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependen…

战地体能训练模拟器

一、名称&#xff1a;战地体能训练模拟器二、整体介绍&#xff1a; 1.体验者身处一个封闭空间的正中央&#xff0c;空间内部表面全部附着显示器&#xff0c;包含地板(因为地板是平的&#xff0c;可以使用投影的方式实现)&#xff0c;经过视角上的设计&#xff0c;体验者就像身处…

java读文件写文件

使用了try-with-resource语法&#xff08;JDK1.7及以上&#xff09;&#xff0c;代码更加便捷 Junit测试样例&#xff0c;先写入文件&#xff0c;再读出来&#xff1a; RunWith(SpringJUnit4ClassRunner.class) ContextConfiguration(locations "classpath:spring-conf…

Markdown入门

Markdown 是一种轻量级的「标记语言」&#xff0c;它的优点很多&#xff0c;目前也被越来越多的写作爱好者&#xff0c;撰稿者广泛使用。看到这里请不要被「标记」、「语言」所迷惑&#xff0c;Markdown 的语法十分简单。常用的标记符号也不超过十个&#xff0c;这种相对于更为…

bean注入失败的几种情况和解决思路:NoSuchBeanDefinitionException: No qualifying bean of type

bean注入失败&#xff0c;无非是两种情况&#xff0c;要么注入的写法出错&#xff0c;要么被注入的Bean未定义。 1.Bean未定义 如果是直接在XML中配置bean标签的时候 检查id和class是否写对 如果是用注解形式申明Bean 先检查Controller、Service、Repository、 Component …

通过AVFoundation框架获取摄像头数据

一、概述 从iOS4开始&#xff0c;AVFoundation框架增加了几个类&#xff0c;AVCaptureDevice、AVCaptureSession等&#xff0c;可以获取摄像头的数据&#xff0c;而不会弹出类似于ImagePicker一样的界面&#xff0c;我们可以将数据转为一张张的图片&#xff0c;然后我们可以即时…

mysql数据库支持emoji表情的详解

mysql存储emoji表情的时候&#xff0c;就会报错&#xff0c;如下&#xff1a; Error updating database. Cause: java.sql.SQLException: Incorrect string value: ‘\xF0\x9F\x98\x8A\xF0\x9F…’ for column ‘这是我表中的字段’ at row 1 初步定位是我的数据库是utf8编码…

CoreText使用介绍

一、概述 1.CoreText是苹果创建的一个用于文字排版的框架&#xff0c;可以实现文字排版、图文混排等复杂的界面效果。从iOS3.2启用。2.一个开源工具类-OHAttributedLabel&#xff0c;就是使用CoreText框架实现的&#xff0c;能够实现一个Label中有不同的文字大小、文字颜色、字…

编程规范:长函数的思考

在工作&#xff0c;我们应该都不想看到非常的长函数。对于一个运行5年左右的项目&#xff0c;极有可能出现这种情况。由于长函数的长、if/else嵌套&#xff0c;导致代码的可读性非常差&#xff0c;这对于项目的维护和开发带来了极大的困难。所以我们应该避免写长函数&#xff0…

用redis实现延迟队列

现在在用的redis实现延迟队列的主流程

iPhone各版本屏幕尺寸

设备宽高对角线逻辑分辨率(point)Scale Factor设备分辨率(pixel)PPI iPhoneWidthHeightDiagonal 3GS2.4 inches (62.1 mm)4.5 inches (115.5 mm)3.5-inch320x4801x320x480163 4(s)2.31 inches (58.6 mm)4.5 inches (115.2 mm)3.5-inch320x4802x640x960326 5c2.33 inches (59.2…

maven更新快照不起作用的解决方法

问题&#xff1a;maven的快照包更新后&#xff0c;调用方使用idea点下面这个地方更新maven&#xff0c;并没有拉到最新的快照 解决方法1 删除本地仓库的快照包&#xff0c;再重新拉一次 解决方法2 下图&#xff0c;这里点进去 下图&#xff0c;这个勾上就行了&#xff0c;再…

iOS中frame和Bounds之间的区别

frame frame是每个view必备的属性&#xff0c;代表的是当前视图的位置和大小&#xff0c;没有设置他&#xff0c;当前视图是看不到的。位置需要有参照物才能确定&#xff0c;数学中我们用坐标系来确定坐标系中的某个点的位置&#xff0c;iOS中有他特有的坐标系&#xff0c;如下…

[数据库]-----mysql数据的冷热分离 第二版

1.前提 这次数据库的冷热分离算是第二次做了 其实之前已经做过一次冷热分离了,涉及到数据库复制时,当时是趋近于业务的(后面会详细讲),整体来讲不是很好用,这次算是重构了吧 做的最终结果还是和前一次一样: 数据库中的订单数据,是每时每刻都在增加 我们认为3个月以内的数据,用…

URL结构

一、概念 URL&#xff1a;统一资源定位符 (Uniform Resource Locator, URL)。 完整的URL由这几个部分构成&#xff1a;scheme://host:port/path?query#hash&#xff1a; scheme&#xff1a;通信协议&#xff0c;常用的有http、https、ftp、mailto等。 host&#xff1a;主机…

[数据库]-----记一次mysql分库的操作(冷热分离)

前提: 1.原有库是mysql数据库,已经根据用户pin分片 2.每片是一主两从 3.主表已经分过表了 4.数据库所在服务器为4C8G 5.库中数据量已经超过千万,而且以每天3万多的数据持续增长,将来每天或许会更多 6.库内数据为订单数据&#xff0c;每时每刻都有新的订单产生&#xff0c;每个…

使用OC进行iOS截屏,同时保证清晰度

一般情况下我们使用如下代码进行截屏&#xff1a;UIWindow *screenWindow [[UIApplication sharedApplication] keyWindow]; UIGraphicsBeginImageContext(screenWindow.frame.size); [screenWindow.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *screenIm…