(1)一个人只要自己不放弃自己,整个世界也不会放弃你.
(2)天生我才必有大用
(3)不能忍受学习之苦就一定要忍受生活之苦,这是多么痛苦而深刻的领悟.
(4)做难事必有所得
(5)精神乃真正的刀锋
(6)战胜对手有两次,第一次在内心中.
(7)好好活就是做有意义的事情.
(8)亡羊补牢,为时未晚
(9)科技领域,没有捷径与投机取巧。
(10)有实力,一年365天都是应聘的旺季,没实力,天天都是应聘的淡季。
(11)基础不牢,地动天摇
(12)写博客初心:成长自己,辅助他人。当某一天离开人世,希望博客中的思想还能帮人指引方向.
(13)编写实属不易,若喜欢或者对你有帮助记得点赞+关注或者收藏哦~
【21】线程池原理
文章目录
1.线程池原理
1.1什么是线程池?为什么要用线程池?
(1)Thread线程,它是操作系统的一个资源,它是有资源消耗的。
(2)使用线程池,事先分配资源完成线程的创建,在执行任务的过程中,创建线程和消毁线程过程就不需要了统一由线程池完成。
(3)如果有一个任务就new一个实例出来,任务的执行时间。
T1:创建
T2:任务的执行时间
T3:线程的消毁时间
(4)线程池就是一上来就把线程的创建和消毁时间去掉,而是让任务直接执行,缩短任务的总的执行时间,即将线程的创建与消费时间交由线程池预先处理,以此节省时间。
(5)线程在操作系统中,是一种稀缺而昂贵的资源,会消耗CPU与内存,哪怕不做任何事情也会消耗内存。
(6)线程执行太多,也会对操作系统造成负担,会导致其他应用程序宕机,因此需要将线程统一的管理起来。
1.1.1线程池接口
(1)Executor
public interface Executor {
/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the {@code Executor} implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
(2)ExecutorService
(3)ThreadPoolExecutor平时用的最多
1.2JDK中的线程池和工作机制
1.2.1线程池的创建
1.2.1.1各个参数含义
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
(1)corePoolSize:当前线程池的核心线程数
(2)maximumPoolSize:当前线程池的最大线程数
(3)keepAliveTime与unit:如果当前线程池的线程数已经超过了corePoolSize,有些线程就空闲下来了,线程没事情做,就由这两个参数控制空闲线程的存活时间,如果空闲线程超过了存活时间,就会被消毁。
(4)workQueue:线程池是专门用来处理任务的,有可能产生往线程池中递交的任务数远远大于最大线程数,比如100,可能在很短的时间内递交了1000个任务进去,这种情况下就将另外900个任务放到阻塞队列中。
(5)handler:如果线程数实在是太多,线程池就可以拒绝,使用拒绝策略。
(6)threadFactory:一般给线程定义个名字
1.2.1.2线程池运行工作机制
(1)当我们定义了corePoolSize的时候,可以认为当前线程池中已启动,可以认为已经预先创建了几个线程,刚开始向线程池提交任务,其中的线程是很空闲的,就可以一个一个的做任务。
(2)当核心线程都被分配了执行任务,再往线程池里面添加任务,此时线程池并不会再创建新的线程去执行任务,而是将任务放到阻塞队列里面。
(3)继续提交任务到线程池,线程池的阻塞队列也被放满时,这时候线程池才会新启线程来执行任务。此时的线程数量一定是小于等于最大线程数的。
(4)如果最大线程数也满了,这时候我们的拒绝策略就会派上用场。这也是线程池的参数对线程工作机制的一个影响。
1.2.1.3线程池运行工作机制小结
- 先在核心线程数范围之内承接任务
- 超过核心线程数承接任务范围,则将任务放入阻塞队列,阻塞队列满了,再在最大线程数之内再启动线程承接任务。
- 超过最大线程数承接任务范围,拒绝策略就开始发挥作用。
1.2.2线程池的拒绝策略
(1)实现类
(2)DiscardOldestPolicy:将最老的给消毁掉,执行当前任务。
(3)AbortPolicy:直接抛出异常
(4)CallerRunsPolicy:让调用者线程去执行任务,就是把任务让给往线程池里面添加任务的线程去做。
(5)DiscardPolicy:把最新提交的任务给扔掉。
(6)如果需要自定义拒绝策略,则可以实现RejectedExecutionHandler接口
1.2.3提交任务
线程池中提供了两种方法为线程池添加任务:
(1)java.util.concurrent.ThreadPoolExecutor#execute
public void execute(Runnable command) {
(2)java.util.concurrent.ExecutorService#submit(java.lang.Runnable, T)
当往线程池中添加任务需要返回结果的时候,使用submit方法实现。
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
1.2.4关闭线程池
提供了两个方法
(1)shutdown
尝试关闭线程池,将没有执行任务的线程进行中断。
java.util.concurrent.ThreadPoolExecutor#shutdown
(2)shutdownNow
无论线程池中的线程有没有在执行任务,全部中断。
java.util.concurrent.ThreadPoolExecutor#shutdownNow
去关闭正在执行任务的线程,它一定会成功吗?
- 不一定。
- 因为线程的中断是一种协作机制,需要看任务中如何处理中断信号的,需要看是否很好的在任务中处理任务的中断。
2.合理配置线程池
(1)主要是配置线程数量的大小与阻塞队列的大小
2.1任务特性
2.1.1CPU密集型
(1)CPU在不停的计算,频繁从内存中取数据进行计算。
(2)配置时,最大线程数量不要超过CPU核心数量。
- CPU核心数的获取
public static void main(String[] args) {
int cpuAvailabelNum = Runtime.getRuntime().availableProcessors();
System.out.println(String.format("当前CPU可用的核心数为:%d",cpuAvailabelNum));
}
- maximumPoolSize:设置为机器CPU核心数+1
2.1.2IO密集型
(1)网络通信或磁盘读写频繁的任务,都称为IO密集型。
(2)网络与磁盘的读写速度要远远低于内存的读写速度的,执行任务的时候会导致线程一直处在磁盘或网络读写过程中,导致其他任务就得等待这个线程执行完成。所以此时的解决方法是,为线程多设置一些线程数。
(3)maximumPoolSize:设置为机器CPU核心数*2
2.1.3混合型
(1)任务既要频繁使用CPU又要频繁使用IO。
(2)如果任务的执行时间CPU处理与IO处理的时间相差不大,可以考虑拆分成两个线程处理,一个进行CPU处理,一个进行IO处理。
(3)如果时间相关非常大,这时候就没有必要考虑拆分线程来做了。
2.2配置
(1)将BlockingQueue<Runnable> workQueue尽量配置成有界阻塞队列,不要配置成无界阻塞队列,因为很有可能撑爆机器,撑爆就是OOM。
U处理,一个进行IO处理。
3.打赏鼓励
感谢您的细心阅读,您的鼓励是我写作的不竭动力!!!