线程池的深入理解

线程池的创建

Executors:工厂类 用于创建并返回不同类型的线程池,但是一般使用ThreadPoolExecutor 去创建。

使用线程池的好处

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

五种线程池类型

  1. newScheduledThreadPool:无限大小的线程池

创建一个定长线程池支持定时及周期性任务执行

  1. newFixedThreadPool:可重用固定线程数的线程池

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

  1. newCachedThreadPool:创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。

  1. newSingleThreadExecutor:只有一个线程的线程池

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去.

  1. threadPool = new ThreadPoolExecutor();//默认线程池,可控制参数比较多

线程池大小分配

线程池究竟设置多大要看你的线程池执行的什么任务了,CPU密集型、IO密集型、混合型,任务类型不同,设置的方式也不一样。 任务一般分为:CPU密集型、IO密集型、混合型,对于不同类型的任务需要分配不同大小的线程池。

1、CPU密集型 尽量使用较小的线程池,一般Cpu核心数+1

2、IO密集型

方法一:可以使用较大的线程池,一般CPU核心数 *2

方法二:(线程等待时间与线程CPU时间之比 + 1)* CPU数目

3、混合型 可以将任务分为CPU密集型和IO密集型,然后分别使用不同的线程池去处理,按情况而定

实现runnable和callable接口的区别

runnable不会返回结果或者抛出检查异常,但是callable可以。

工具类 Executors 可以实现 Runnable 对象和 Callable 对象之间的相互转换。

执行execute和 submit方法的区别

execute用于提交不需要返回值的任务 无法判断是否成功

submit 用于提交需要返回值的任务。会返回一个Futrue类型的对象,用来判断任务是否成功。

线程池的一个基本流程

线程池内部是通过队列+线程实现的,当我们利⽤线程池执⾏任务时:

  1. 如果此时线程池中的线程数量⼩于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。

  1. 如果此时线程池中的线程数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放⼊缓冲队列。

  1. 如果此时线程池中的线程数量⼤于等于corePoolSize,缓冲队列workQueue满,并且线程池中的数量⼩于maximumPoolSize,建新的线程来处理被添加的任务。

  1. 如果此时线程池中的线程数量⼤于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。

  1. 当线程池中的线程数量⼤于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终⽌。这样,线程池可以动态的调整池中的线程数

实际上执行它的流程 是 三部分 核心线程数 任务队列 和 最大线程数

有任务 创建线程执行,当线程数等于核心 线程数则将新来的任务放入队列等待,当任务队列满了时则会继续创建新线程执行任务 ,当到达最大线程数的时候 进入shutdown 不在接受新任务

线程执行任务两种情况 1是直接创建新线程直接执行这个任务 2是从工作队列中获取

线程池的五种状态

running:能够接收新任务,以及对已添加的任务进行处理。线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0

SHUTDOWN:不接受新任务,处理已有的任务

STOP:调用线程池的shutdownNow()接口时,不接受新任务,不处理已经添加的任务,中断正在处理的任务

tidying:所有任务已终止,会执行钩子函数terminated(),需要重写

TERMINATED:彻底终止。 terminated

核心参数

  • corePoolSize 代表核⼼线程数,也就是正常情况下创建⼯作的线程数,这些线程创建后并不会消除,⽽是⼀种常驻线程。

  • maxinumPoolSize 代表的是最⼤线程数,它与核⼼线程数相对应,表示最⼤允许被创建的线程数,⽐如当前任务较多,将核⼼线程数都⽤完了,还⽆法满⾜需求时,此时就会创建新的线程,但是线程池内线程总数不会超过最⼤线程数。

  • keepAliveTime 、 unit 表示超出核⼼线程数之外的线程的空闲存活时间,也就是核⼼线程不会消除,但是超出核⼼线程数的部分线程如果空闲⼀定的时间则会被消除,我们可以通过setKeepAliveTime 来设置空闲时间。

  • workQueue ⽤来存放待执⾏的任务,假设我们现在核⼼线程都已被使⽤,还有任务进来则全部放⼊队列,直到整个队列被放满但任务还再持续进⼊则会开始创建新的线程。

  • ThreadFactory 实际上是⼀个线程⼯⼚,⽤来⽣产线程执⾏任务。我们可以选择使⽤默认的创建⼯⼚,产⽣的线程都在同⼀个组内,拥有相同的优先级,且都不是守护线程。当然我们也可以选择⾃定义线程⼯⼚,⼀般我们会根据业务来制定不同的线程⼯⼚。

  • Handler 任务拒绝策略,有两种情况,第⼀种是当我们调⽤ shutdown 等⽅法关闭线程池后,这时候即使线程池内部还有没执⾏完的任务正在执⾏,但是由于线程池已经关闭,我们再继续想线程池提交任务就会遭到拒绝。另⼀种情况就是当达到最⼤线程数,线程池已经没有能⼒继续处理新提交的任务时,这也就是拒绝。

四种拒绝策略(饱和策略) 线程已满

主要有4种拒绝策略:

  1. AbortPolicy:直接丢弃任务,抛出异常,这是默认策略

  1. CallerRunsPolicy:只用调用者所在的线程来处理任务

  1. DiscardOldestPolicy:丢弃等待队列中最旧的任务,并执行当前任务

  1. DiscardPolicy:直接丢弃任务,也不抛出异常

阻塞队列(用在消费者生产者模型)

阻塞队列 是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用 。

java线程池中队列的常用类型有哪些?

  1. ArrayBlockingQueue 是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。

  1. LinkedBlockingQueue 一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue 。

  1. SynchronousQueue 一个不存储元素的阻塞队列。

  1. PriorityBlockingQueue 一个具有优先级的无限阻塞队列。PriorityBlockingQueue 也是基于最小二叉堆实现

  1. DelayQueue 只有当其指定的延迟时间到了,才能够从队列中获取到该元素。 DelayQueue 是一个没有大小限制的队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值