美文网首页
Java并发编程,关于那些厉害得不得了的线程池...

Java并发编程,关于那些厉害得不得了的线程池...

作者: 世说烟雨亦话悲凉 | 来源:发表于2018-10-23 10:54 被阅读0次

    本文编辑于 2018/10/23 不可能侵权,所以不删

    ReStartLin


    前言

    众所周知,在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:核心线程数,顾名思义 就是线程池中重中之重的线程的数目,被归类在核心线程池中的线程即使没有在使用,也不会被销毁. 当然如果你想它也被销毁复用的话,,,那就设置allowCoreThreadTimeOuttrue
    maximumPoolSize:线程池所能容纳的最大线程数。超过这个数的线程将被阻塞。需要注意的是:任务队列为没有设置大小的---LinkedBlockingDeque时,这个值无效。因为LinkedBlockingDeque不设置大小默认是无限大的...所以装都装不完,就不会触发这个参数了.
    keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止,配合TimeUnit来使用.
    unit:参数 keepAliveTime 的时间单位,有七种取值,在TimeUnit类中有七种静态属性
    workQueue:顾名思义是任务队列,取值类型参考上文.

    最后一个参数没啥好说的,主要是前面四个参数是配合使用的.

    keepAliveTimeunit是用来指定线程在空闲多久时间之后会被销毁用的,那么配合其他的参数一起会是怎么样的呢,,举个例子:

    有这么一间工厂,靠生产谋生(这好像是废话_)/. 这家工厂一创立就用了10个员工,假设这家工厂实行永久雇佣制,那么这就是所谓的corePoolSize核心线程了.
    继续假设每个员工每次只能着手完成一个任务,那么任务的来源就是流水线了. 那么这条流水线上能放多少个任务呢?这就是靠工作队列workQueue所指定的长度来决定的,下文假设是20;
    今天是个好日子,厂里开张得到了20个订单,准备着手生产.
    10个员工马不停蹄地领走了10个订单进行生产,剩下的10个订单就只能留在流水线上等待被执行了.

    时间流失,又有那么一天,但是这天却很糟糕,厂里不小心接到了50个订单,,,我的天
    10个员工拿走10个订单,流水线上放着20个订单,,,,那还剩20个订单进不去呀,,这可怎么办 这样会造成工厂崩溃的
    老板灵机一动:拿钱(消耗资源)去请临时员工!! 就这样请来了20个临时员工(线程),这样就解决了燃眉之急了2333

    往后几天工作量又平稳了下来,10个员工足以胜任,不能白养着临时员工呀,开掉. 这个临时员工存在的时长呢,就是keepAliveTimeunit

    故事说到这里,想必你们都捋清楚了吧, 核心线程就是核心员工,不会被开除(回收),那么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的核数

    如果有误,请斧正

    相关文章

      网友评论

          本文标题:Java并发编程,关于那些厉害得不得了的线程池...

          本文链接:https://www.haomeiwen.com/subject/lceozftx.html