本文编辑于 2018/10/23 不可能侵权,所以不删
前言
众所周知,在java的后端开发中,少不了和线程打交道,但同时线程的创建和销毁也是很耗费资源的,那么如何提高并发的效率呢? 为了解决这个问题,java就给我们带来了
concurrent
并发包
前言的前言
既然说到并发,那么中点内容就是线程池了,但是线程池的底层实现又依赖于队列
,队列
才是对任务进行分发执行的核心...
说到队列,粗略地列表一下就是以下几种:
1.ArrayDeque, (数组双端队列)
2.PriorityQueue, (优先级队列)
3.ConcurrentLinkedQueue, (基于链表的并发队列)
4.DelayQueue, (延期阻塞队列)(阻塞队列实现了BlockingQueue接口)
5.ArrayBlockingQueue, (基于数组的并发阻塞队列)
6.LinkedBlockingQueue, (基于链表的FIFO阻塞队列)
7.LinkedBlockingDeque, (基于链表的FIFO双端阻塞队列)
8.PriorityBlockingQueue, (带优先级的无界阻塞队列)
9.SynchronousQueue (并发同步阻塞队列)
还挺多,具体看业务场景选择使用即可.深入了解还需要查找相关内容
线程池
前言说完就说说我们的主角线程池了...
线程池的目的在于:
1、降低资源的消耗,通过重复利用已创建的线程降低线程创建和销毁所造成的消耗
2、提高相应速度
3、提高线程的可管理性
java也给我们便利地带来了对应的实现操作类.
Executor
框架
CachedThreadPool
:创建一个可缓存线程池,如果线程池的长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
FixedThreadPool
创建一个定长线程池, 可控制线程最大并发数,超出的线程会在队列中等待
ScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务的执行
SingleThreadExecutor
创建一个单线程化的线程池,他只会用唯一的工作线程来执行任务,保证所有的任务按照指定顺序FIFO LIFO优先级 等 执行
有趣的是,这几个线程池的最顶层实现其实是构造函数的参数不同而已...
Executor 框架的最顶层实现是ThreadPoolExecutor类 Executors工厂类中提供的
ThreadPoolExecutor
这里说说ThreadPoolExecutor
构造函数中常用的5
个参数,其他还请自行查询资料.
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
一个一个来说明:
corePoolSize
:核心线程数,顾名思义 就是线程池中重中之重的线程的数目,被归类在核心线程池中的线程即使没有在使用,也不会被销毁. 当然如果你想它也被销毁复用的话,,,那就设置allowCoreThreadTimeOut
为true
maximumPoolSize
:线程池所能容纳的最大线程数。超过这个数的线程将被阻塞。需要注意的是:当
任务队列为没有设置大小的---LinkedBlockingDeque
时,这个值无效。因为LinkedBlockingDeque
不设置大小默认是无限大的...所以装都装不完,就不会触发这个参数了.
keepAliveTime
:表示线程没有任务执行时最多保持多久时间会终止,配合TimeUnit
来使用.
unit
:参数 keepAliveTime
的时间单位,有七种取值,在TimeUnit类中有七种静态属性
workQueue
:顾名思义是任务队列,取值类型参考上文.
最后一个参数没啥好说的,主要是前面四个参数是配合使用的.
keepAliveTime
和unit
是用来指定线程在空闲多久时间之后会被销毁用的,那么配合其他的参数一起会是怎么样的呢,,举个例子:
有这么一间工厂,靠生产谋生(这好像是废话
_
)/. 这家工厂一创立就用了10个员工,假设这家工厂实行永久雇佣制,那么这就是所谓的corePoolSize
核心线程了.
继续假设每个员工每次只能着手完成一个任务,那么任务的来源就是流水线了. 那么这条流水线上能放多少个任务呢?这就是靠工作队列workQueue
所指定的长度来决定的,下文假设是20;
今天是个好日子,厂里开张得到了20个订单,准备着手生产.
10个员工马不停蹄地领走了10个订单进行生产,剩下的10个订单就只能留在流水线上等待被执行了.
时间流失,又有那么一天,但是这天却很糟糕,厂里不小心接到了50个订单,,,我的天
10个员工拿走10个订单,流水线上放着20个订单,,,,那还剩20个订单进不去呀,,这可怎么办 这样会造成工厂崩溃的
老板灵机一动:拿钱(消耗资源)去请临时员工!! 就这样请来了20个临时员工(线程),这样就解决了燃眉之急了2333
往后几天工作量又平稳了下来,10个员工足以胜任,不能白养着临时员工呀,开掉. 这个临时员工存在的时长呢,就是
keepAliveTime
和unit
故事说到这里,想必你们都捋清楚了吧, 核心线程就是核心员工,不会被开除(回收),那么
maximumPoolSize
最大线程数指的就是核心员工加临时员工的数目了,如果任务总量超过了这个值,就会造成系统崩溃的(throws exception).
举个栗子:
ThreadPoolExecutor(1,2,10l,TimeUnit.SECONDE,new LinkedBlockingDeque<String>(5));
极限状态下,任务开始执行,核心线程取走1
个任务,队列存放5
个任务,临时线程为2-1
一个,临时创建执行一个任务,那么最大能同时容纳``1+5+1```个任务,超过这个数目就会报错了,,指定临时线程在空闲 10s之后就会被销毁
关于线程数的设置
核心线程数与最大线程数相同.
考虑I/O密集
型任务和CPU密集
型任务,这两者具体是什么需要查阅相关资料
一般来说,,
CPU密集
型任务 一般设置线程数为:CPU核心数或者CPU核心数+1
I/O密集
型任务 一般设置线程数为:2* cpu的核数
如果有误,请斧正
网友评论