java线程池

需要回答的几个问题?

1. 为什么需要线程池?

2. 线程池类图、线程池的核心参数、线程池相关依赖类?

3. 线程池怎么使用?

4. 使用线程池需要注意什么?

5. 线程池的实际使用源码分析

6. 模拟服务器应用线程池

一、为什么要使用线程池

线程池是用来管理一组线程,并且可以提供管理线程一些额外功能。每当有一个新的任务则使用线程池,让线程池进行去处理这个任务,可以提高应用的吞吐量。

1. 这样就可以避免来一个任务然后进行创建一个线程T1、然后再关闭一个线程T3,因为T1 + T3的时间可能大于T2的执行时间,这样带来额外的时间效率很低。

2. 如果5000个请求,都开辟线程,线程是需要具有独立空间的,很可能直接oom,使用线程池就可以避免高并发。

 

二、线程池类图的架构

executor提供了execute()方法,也就是执行,把任务的提交和软件的执行给分离。

executorServer就相当于一个线程池服务,提供了管理线程池的生命周期以及更加方便的任务提交。

ThreadPoolExector是 用于管理java的多线程,包括线程的空闲处理机制以及拒绝机制也就是一个可以通过传入的参数配置创建一个自定义具体的功能的线程池。

 

 1.1 ThreadPoolExector线程池的相关参数

 

 

new ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler)
     corePoolSize:      线程池维护线程的最少数量 (core : 核心) 默认
     maximumPoolSize:   线程池维护线程的最大数量 
     keepAliveTime:     线程池维护线程所允许的空闲时间 代表空余的线程存活的时间,当多余的线程(当前线程 减去 核心线程 = 多余线程)完成任务的时候,需要多长时间进行回收
     unit:               线程池维护线程所允许的空闲时间的单位
     workQueue:          线程池所使用的缓冲队列,用来放置需要处理的任务,该任务可以实现Runnable或者callable
     handler:            线程池对拒绝任务的处理策略  RejectedExecutionHandler  默认使用 new AbortPolicy();

 

 workQueue是一个堵塞式队列,线程池可以使用take和put进行取任务和放任务,之所以线程是可以进行循环使用是因为核心线程一直在循环向work执行take操作,多余线程使用take(keepalive)也一直获取任务,如果在一定时间没有获得任务则进行清楚。

 

RejectedExecutionHandler提供了四种方式来处理任务拒绝策略

1、直接丢弃(DiscardPolicy)

2、丢弃队列中最老的任务(DiscardOldestPolicy)。

3、抛异常(AbortPolicy)

4、将任务分给调用线程来执行(CallerRunsPolicy)。

 三、线程池的处理流程

ThreadPoolExecutor执行execute方法分下面4种情况:

1、如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(执行这一步骤需要获取全局锁)。
2、如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。
3、如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(执行这一步骤需要获取全局锁)
4、如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用
RejectedExecutionHandler.rejectedExecution()方法。

 

 

五、Exectors 执行器工具类

Exectors  提供已经封装好的几个可以使用于特定场景的几个线程池

fiexdThreadPool()\cacheThreadPool\signalThreadPool\SchduledThreadPool

https://my.oschina.net/sdlvzg/blog/2222136   使用方式。

 

 

分类:

1.        创建无大小限制的线程池(newCachedThreadPool())

2.        创建固定大小的线程池(newFixedThreadPool(int nThreads))

3.        单线程池 (newSingleThreadScheduledExecutor())

4.  创建定时调度池 (newScheduledThreadPool(int corePoolSize)

前面三个使用linkedBlockingQueue,后面一个使用是DelayBlockingQueue

五、线程池的IO密集型和cpu密集型

核心线程是应该充分利用cpu的,线程是cpu最小的调度单位。

我们应该程序应该充分地考虑到cpu的使用情况,也需要考虑到上下文线程的切换所需的时间,这个时间对于cpu进行计算的时间是非常多的。

大量的线程开启在线程池必然会具有大量的 线程之间的切换。

对于Cpu密集型的任务应该开启的   线程=cpu核 + 1

对于IO密集型的任务应该开启的  线程 = cpu核/(1-堵塞系数) io密集型也就是去访问网络,操作数据库等,需要堵塞进行返回结果,这个时候线程是不消耗cpu的

这样的话,cpu就空下来了,那么久可以多启动一些线程进行使用cpu,上下文的切换时间是肯定小于堵塞所需的时间,但是太多的线程开启也是不可的。

 

posted @ 2019-10-23 22:33  逆水不进则退  阅读(215)  评论(0)    收藏  举报