您的位置:首页 > 其它

线程池

2016-08-21 10:39 267 查看
考官的问题总是让我们这些人觉得真心受虐,他问线程池你用过没,我说工作当中没有怎么用到,都是自学。然后他又问,线程池里面的锁是什么,我说是我说是重入的互斥锁。然后说说原理,什么线程池里面有一个队列,任务进来如果没有达到coresize的时候会直接执行,如果到达maxsize的时候会放入队列当中去。如果队列满了就会有三种淘汰机制,一种是抛异常、一种是啥也不管的,一种是忘了。。。(其实是用自身的线程继续执行任务)。然后他问队列都分哪些啊?我蒙了,因为我忘了。然后就没有然后。。。我很讨厌在这种情况下还让我坐等下一个面试官,直接请我走可能觉得不礼貌吧。哎,礼不礼貌我都不在乎。因为我的人生格言就是:人在路上,毫不畏惧。

接下来我就来梳理下这方面的知识,算是对知识的一些总结。要想真正的理解线程池,得从他的“零部件”入手。有哪些
1、队列
2、线程
3、线程管理器
4、满载处理器

队列是放置任务的,线程是消耗任务的,线程管理器是管理线程如何消耗任务的。最后一个就是当任务超过线程池处理能力的时候怎么处理的。
#队列
队列分为非阻塞队列和阻塞队列。
##阻塞队列
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。
阻塞队列提供了四种处理方法
--|--|--|--|--|
方法|抛出异常|返回特殊值|一直阻塞|超时退出|
--|
插入方法|add(e)|offer(e)|put(e)|offer(e,time,unit)
移除方法|remove()|poll()|take()|poll(time,unit)
检查方法|element()|peek()|不可用|不可用

JDK7提供了7个阻塞队列。分别是

ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
DelayQueue:一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue:一个不存储元素的阻塞队列。
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
队列不是文本重点,暂且了解到这里,后期会针对每一个的实现细节进行交代,主要侧重点是各个队列的实现、区别、试用场景。
##非阻塞队列
在并发编程中我们有时候需要使用线程安全的队列。如果我们要实现一个线程安全的队列有两种实现方式一种是使用阻塞算法,另一种是使用非阻塞算法。使用阻塞算法的队列可以用一个锁(入队和出队用同一把锁)或两个锁(入队和出队用不同的锁)等方式来实现,而非阻塞的实现方式则可以使用循环CAS的方式来实现。典型的有ConcurrentLinkedQueue。
#线程
线程是线程池里面的基本元素,是用来处理任务的“工人”。我们平时实现一个线程主要包括了以下几种方法。
1、通过实现Runnable接口来创建多线程
2、通过扩展Thread类来创建多线程。
3、通过实现Callable接口来创建多线程。
线程的生命周期包括五个阶段:
新建(new Thread)
就绪(runnable)
运行(running)
死亡(dead)
堵塞(blocked)
线程池就是要管理线程,掌管线程的生命周期,调度线程的执行任务,成为线程的最高“统帅”。
#线程管理器
有了线程就要有“指挥官”管理这些线程,包括线程生命周期的每个阶段。拿类ThreadPoolExecutor举例。
ThreadPoolExecutor在创建的时候需要以下参数

corePoolSize:线程池最开始需要创建的线程数量。
maximumPoolSize:线程池允许创建的最大线程数量。
keepAliveTime:当线程数量超过corePoolSize以后,那些超过corePoolSize的线程最大的空闲时间,如果超过这个时间还没来任务,那么他们就将被销毁。
unit:时间单位,默认都是毫秒
workQueue:工作队列。
threadFactory:创建线程的线程工厂。
handler:当线程池内的线程都饱和运转,工作队列也挤满了任务,这个时候再来任务线程池就要用handler来处理。
IllegalArgumentException:如果corePoolSize<0,maximumPoolSize<=0或者corePoolSize>maximumPoolSize ,将要抛出此类型错误
NullPointerException:如果workQueue、threadFactory、handler任意一个为空,要抛出空指针错误。


具体流程如下:

1)当池子大小小于corePoolSize就新建线程,并处理请求

2)当池子大小等于corePoolSize,把请求放入workQueue中,池子里的空闲线程就去从workQueue中取任务并处理

3)当workQueue放不下新入的任务时,新建线程入池,并处理请求,如果池子大小撑到了maximumPoolSize就用RejectedExecutionHandler来做拒绝处理

4)另外,当池子的线程数大于corePoolSize的时候,多余的线程会等待keepAliveTime长的时间,如果无请求可处理就自行销毁
#满载处理器
线程池接受任务创建线程执行,在外界任务压力过大的时候早晚有一天“体不能支”,无法处理外来任务的时候,这个时候需要处理这些暂时无法处理的任务。这里的满载处理器就是处理这种情况的工具。那么如何处理这些无法处理的任务呢?

jdk提供了四种满载处理机制

AbortPolicy:抛异常RejectedExecutionException
CallerRunsPolicy:用提交任务的线程本身去处理任务。
DiscardPolicy:直接拒绝任务,什么也不做。
DiscardOldestPolicy:抛弃执行最长的任务,尝试执行新来的任     务。如果线程池被关闭,任务就会被抛弃不作处理。


至此线程池的东西大体就交代了一遍。当然这只是泛泛的说。要想真正理解线程池需要对阻塞队列有深刻的理解,基于业务指导合适的满载处理器,会调整线程池的参数适应业务变化的需要。

#参考文献
http://ifeve.com/concurrentlinkedqueue/
http://ifeve.com/java-blocking-queue/
/detail/2526319763.html
/detail/2502714680.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: