上一篇 <<<CyclicBarrier屏障
下一篇 >>>并发队列
线程池的作用
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,
还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用
第四:提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。
线程池的创建方式
1.newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
2.newFixedThreadPool :创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
3.newScheduledThreadPool :创建一个定长线程池,支持定时及周期性任务执行。
4.newSingleThreadExecutor :创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
为什么需要开发者自己基于ThreadPoolExecutor构造函数封装
答案:
a.因为底层都是基于无界队列实现, 缓存线程队列可以无限的存放容量大小,有可能会内存溢出。
b.线程池中最大线程数是无效的。
线程池原理剖析
1、判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程。
2、线程池判断工作队列是否已满,如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。
3、判断线程池里的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。
核心参数
corePoolSize: 核心池的大小。 当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中
maximumPoolSize: 线程池最大线程数,它表示在线程池中最多能创建多少个线程;
keepAliveTime: 表示线程没有任务执行时最多保持多久时间会终止。
unit: 参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:
workQueue 缓存线程队列。
threadFactory 线程池内部创建线程所用的工厂
handler 任务无法执行时的处理器,也就是拒绝策略回调方法
为什么要用阻塞队列
使用阻塞队列,可以复用,降低创建线程的消耗
核心线程数和最大线程数的区别
核心线程数是最大运行线程数
最大线程数是最大创建线程数
核心不能大于最大
线程池的状态
a.RUNNING:线程池能够接受新任务,以及对新添加的任务进行处理。
b.SHUTDOWN:线程池不可以接受新任务,但是可以对已添加的任务进行处理。
c.STOP:线程池不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
d.TIDYING:当所有的任务已终止,ctl记录的"任务数量"为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行构造函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
e.TERMINATED:线程池彻底终止的状态。
线程池的拒绝场景及策略
拒绝场景:
a.当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务
b.当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务。
拒绝策略:
a.AbortPolicy【默认】 丢弃任务,抛运行时异常
b.CallerRunsPolicy 执行任务
c.DiscardPolicy 忽视,什么都不会发生
d.DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
e.实现RejectedExecutionHandler接口,可自定义处理器
线程拒绝后如何处理保证不丢失?
使用try方式捕获记录到日志中,后期定时或者人工补偿。
线程池如何配置
IO密集型:大量IO操作,容易造成IO阻塞,可配置多线程,每个阻塞互不影响。
可配置多个线程 2*CPU盒数—任务管理器就可以看见
CPU密集型:线程没等待,不会产生阻塞时,线程数等于CPU盒数+1
execute和submit方法的区别
a、submit可以支持多种,execute只支持runnable
b、submit有Future返回值,execute返回void
Executors和Executor区别
Executor是接口类,ThreadPoolExecutor就是他的子类
public interface Executor {
void execute(Runnable command);
}
工具类,提供很多静态的方法
public class Executors {
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
……
}
手写线程池的思路
相关文章链接:
<<<多线程基础
<<<线程安全与解决方案
<<<锁的深入化
<<<锁的优化
<<<Java内存模型(JMM)
<<<Volatile解决JMM的可见性问题
<<<Volatile的伪共享和重排序
<<<CAS无锁模式及ABA问题
<<<Synchronized锁
<<<Lock锁
<<<AQS同步器
<<<Condition
<<<CountDownLatch同步计数器
<<<Semaphore信号量
<<<CyclicBarrier屏障
<<<并发队列
<<<Callable与Future模式
<<<Fork/Join框架
<<<Threadlocal
<<<Disruptor框架
<<<如何优化多线程总结