线程池

一.为什么使用线程池?
  • 降低资源的消耗
  • 线程的复用
  • 控制最大的并发数
  • 管理线程
二.执行流程

线程池_第1张图片

  1. 在创建了线程池后,开始等待请求
  2. 当调用execute()方法添加一个请求任务时,线程池会做出如下判断:
    • 如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务:
    • 如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列:
    • 如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
    • 如果队列满了且正在运行的线程数量大于或等于1Size,那么线程池会启动饱和拒绝策略来执行。
  3. 当一个线程完成任务时,它会从队列中取下一个任务来执行。
  4. 当一个线程无事可做超过一定的时间(keepA1iveTime)时,线程会判断:
    • 如果当前运行的线程数大于coreP佣1Size,那么这个线程就被停掉。
    • 所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

 

这里放一个b站狂神说的视频链接,将线程池比喻成银行柜台进行讲解:https://www.bilibili.com/video/BV1B7411L7tE?p=23

线程池_第2张图片

三.3大方法,7大参数,4大拒绝策略

三大方法

  • Executors.newFixedThreadPool(10); //创建固定线程数的线程池
  • Executors.newSingleThreadExecutor(); //创建一个线程数的线程池
  • Executors.newCachedThreadPool();;//创建一个可扩展的线程池(可伸缩)
    • 这三种方式底层都是使用 new ThreadPoolExecutor()
    • 我们开发中使用哪一种?都不用,而是使用new ThreadPoolExecutor(7大参数),因为可自定义参数 

七大参数

  • corePollSize:核心线程数
  • maximumPoolSize:最大线程数
  • keepAliveTime:空闲的线程(除核心线程外的线程)保留的时间。(保存多级后销毁,核心线程不会销毁)
  • TimeUnit:空闲线程的保留时间单位。
  • BlockingQueue:阻塞队列,存储等待执行的任务。
  • ThreadFactory:线程工厂,用来创建线程,一般默认即可
  • RejectedExecutionHandler:设置拒绝策略

四大拒绝策略  

  • new ThreadPoolExecutor.AbortPolicy();//默认,队列满了丢任务,抛出异常
  • new ThreadPoolExecutor.DiscardPolicy();//队列满了丢任务,不抛出异常【如果允许任务丢失这是最好的】
  • new ThreadPoolExecutor.DiscardOldestPolicy();//将最早进入队列的任务删除,之后再尝试加入队列
  • new ThreadPoolExecutor.CallerRunsPolicy();//如果添加到线程池失败,那么主线程会自己去执行该任务,回退
四.解惑线程池的一些问题

线程池中阻塞队列的作用? 主要可以阻塞,不至于放弃线程。

  • 阻塞队列就是一个队列数据结构,当队列是空的,从队列中"获取"元素的操作将会被阻塞; 当队列是满的,从队列中添加元素的操作将会被阻塞。
  • 这是一般队列不能达到的效果,好处是我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,,因为这一切BlockingQueue 都给你一手包办了。

为什么要先添加到队列而不是先创建最大线程?

  • 因为创建新线程时,要维护一个全局锁,这个时候就会影响性能。
  • 把阻塞队列当作一个缓存区,避免频繁的创建和销毁线程(这里主要指非核心线程),这也是线程池的一个初衷。

线程池中线程复用的原理?

  • 线程池将线程任务进行解耦,线程就是线程,任务就是任务。一个线程可以循环执行多个任务
  • 原理就是线程池对Thread进行封装,每次执行线程不是调用Thread.start(),而是让线程一直在做循环任务,不停的检查是否有任务需要执行,也就是调用run()执行任务。把所有的run() 都当作一个普通的方法进行。

 

 

寄语:先努力让自己发光,对的人才能迎着光而来。

你可能感兴趣的:(线程池)