1.自定义线程池
2.ThreadPoolExecutor
3.设计模式工作线程
4.fork/join线程池
让有限的工作线程轮流异步处理无限多的任务,也可以归为分工模式,典型实现就是线程池,也体现了经典设计模式中的享元模式。
注意:不同任务类型应该使用不同的线程池,这样能够避免饥饿现象,且效率上得到合理的分配
线程饥饿现象:本是上是出现多任务混合在同一个线程池中,如果出现相互影响的状况出现类似死锁的问题,有相互关联的相关任务不作为同一线程池处理,而是单独开辟线程池
本质:运用内存共享的原理,去有效支撑大量的细颗粒度的对象
享元工厂,一个享元工厂,用来创建并管理对象,他主要是用来确保合理地共享对象,当用户请求一个对象时,由工厂提供一个已创建的对象示例或者创建一个
享元对象:一个重复的对象
使用场景:如果一个程序对于某个对象进行大量应用,且使用生命周期短,可以考虑采取享元模式进行复用。
处理器核数:
线程核心数是一种执行资源,资源数量就是核的个数,应用程序的线程就是服务请求数,而操作系统的作用如何调配有限的资源来服务更多的请求,这就是进程调度的概念,一般情况下,服务线程相对公平的分配到核上运行,并且在时间片上轮流使用,这就是所谓的并发执行。
比如系统是4核,如果3个线程,分配到3个核上,8哥线程,每个核分配两个线程执行,10个线程,有些核跑3个,有些跑两个。
所以,并非线程数量越大,速度越快,线程数量太过于庞大会导致各种内存问题,因为一个线程的开辟还会涉及到线程的上下文的应用。
问题:创建多少线程合适?
CPU密集型运算:通常采用CPU核数+1能够实现最优的CPU利用率,+1是保证当线程由于缺页时故障或其他原因导致暂停,额外的这个线程能够顶上去,保证CPU始终周期不被浪费。
I/O密集型运算:
CPU不总是处于繁忙状态,例如,当你执行业务计算时,这时候会使用CPU资源,但当你执行IO操作,或者远程的RPC调用时,包括进行数据库操作等,这个时候CPU会闲下来,你可以利用多线程提高他的利用率。
经验公式如下:
线程数=核数 * 期望CPU利用率 * 总时间(CPU计算时间+等待时间)/CPU计算时间例如:4核CPU,计算时间时50%,其他等待时间是50%,期望CPU被100%利用,套用公式4*100%*100%/50%=8
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue< Runnatle > workQueue, ThreadFactory threadFactory, RejectedExecutionMandler handler)
corePoolSize核心线程数目
maximumPoolSize最大线程数目
keepAliveTime生存时间-针对救急线程
workQueue阻塞队列
threadFactory线程工厂,可以为线程创建时起个好名字
handler拒绝策略
maxSize最大线程 - coreSize核心线程 = 救急线程
核心线程可以1个执行1个等待,当来第5个任务的时候就会扩,产生急救线程。
利用位运算合并整型变量在并发下的意义:如果两个数都是原子变量,那么合并之后,只需要对于一个原子进行CAS操作,而不是两个CAS操作
ThreadPoolExecutor自己已经提供了4个拒绝策略:
CallerRunsPolicy:在任务被拒绝添加后,会调用当前线程池的所在的线程去执行被拒绝的任务,这个策略的缺点就是可能阻塞主线程。
AbortPolicy:默认的拒绝策略就是AbortPolicy,直接抛出异常,抛出个RejectedExecutionException异常,也不执行这个任务了
DiscardPolicy:这个东西什么都没干
DiscardOldestPolicy:当任务被拒绝添加时,会抛弃任务队列中最旧的任务也就是最先加入队列的,再把这个新任务添加进去。
1.救急线程
性能提升
2.状态设置
对于线程池的管理
3.原子合并
利用位运算进行CAS的优化
4.工厂模式的应用
fork/join时JDK1.7后加入的新的线程池实现,他主要体现的是分治思想,适用于能够进行任务拆分的CPU密集型运算,他是为了处理大数据诞生的,所谓任务拆分,是将一个大任务拆分为算法上相同的小任务,直至不能够拆分可以直接求解,跟递归相关的一些计算,如归并排序,斐波那契数列,都可以进行分治完成,fork/join在分治的基础中加入了多线程,可以把每个任务的分解和合并交给不同的线程来完成,进一步提升运算效率,fork/join默认会创建于CPU核心数大小相同的线程池,最常见业务,对于文件夹的操作。