美文网首页
线程池笔记

线程池笔记

作者: zmy26 | 来源:发表于2022-06-11 12:15 被阅读0次

前言:线程池相关的所有类都在java.util.concurrent包下面

一、线程池的继承关系:

二、重点介绍ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,

                          int maximumPoolSize,

                          long keepAliveTime,

                          TimeUnit unit,

                          BlockingQueue workQueue,

                          ThreadFactory threadFactory,

                          RejectedExecutionHandler handler) {

    xxx

}

1、线程池工作原理:

a、提交优先级:核心线程数 > 工作队列 > 非核心线程数

     创建线程达到核心线程数后,再来任务会放入工作队列中,当工作队列也满了,再来任务会依据核心线程数的数量创建线程,当非核心线程数也满了就会执行拒绝策略

b、执行优先级:核心线程数 > 非核心线程数 > 工作队列

     执行线程任务时,优先执行核心线程数的任务,其次非核心线程创建的任务,最后才是工作队列中等待的任务

2、重点解释一下ThreadPoolExecutor构造函数的几个参数,对理解线程池非常重要

(1)、int corePoolSize:核心线程数,也就是线程池创建后会一直保存几个线程

(2)、int maximumPoolSize:最大线程数,最大线程数 = 核心线程数 + 非核心线程数(没有这个参数,但在源码中存在的概念)

(3)、long keepAliveTime:非核心线程数存活的时间

(4)、TimeUnit unit:非核心线程数存活时间的单位,例如毫秒,秒等

(5)、BlockingQueue workQueue:工作队列

(6)、ThreadFactory threadFactory:创建线程的工厂,一般用默认

(7)、RejectedExecutionHandler handler:拒绝策略,当核心线程数满了,工作队列满了,非核心线程数也满了,此时再有业务调用线程池创建线程时的策略,比如策略可以设置抛异常,可以设置哪个线程调用线程池执行任务哪个线程处理,可以设置丢弃最后一次任务等。

三、常用创建线程池的方法,在这里涉及到一个设计模式工厂模式,通过调用Executors来创建线程池,而不是直接new线程池(值得学习):

1、Executors.newCachedThreadPool():这种方式创建的线程池我们进源码看下

核心线程数0:表示线程池创建时不创建任何线程

最大线程数Integer.MAX_VALUE即21亿多:表示非核心线程数趋近于无限

非核心线程数存活时间60:

非核心线程数存活时间单位秒:表示创建出来的线程60s内没有被使用,就自动销毁

工作队列SynchronousQueue同步队列:表示队列中只有一个位置,只会有一个任务在等待,其他任务会被创建线程池立马执行

缺点:由于创建线程数趋近于无限大,会造成cpu100%

2、Executors.newFixedThreadPool(5):这种方式创建的线程池我们进源码看下

核心线程数由外部传参决定:核心线程数和最大线程数一致

最大线程数由外部传参决定:核心线程数和最大线程数一致

非核心线程数存活时间0:

非核心线程数存活时间单位毫秒:表示创建出来的线程只要不被使用,就立马销毁

工作队列LinkedBlockingQueue链表队列:我们进LinkedBlockingQueue的构造方法看下

无参构造,默认是int类型的最大值21亿+

总结:newFixedThreadPool这个方式创建出来的线程,创建时传参是几就会创建几个线程,后来的任务会被放到一个21亿+的队列中等待,没有非核心线程的概念,也就没有非核心线程的存活时间(所以源码中存活时间是0)

缺点:如果任务一直源源不断到来,队列中将一直存储,队列占用内存,会导致内存溢出oom

3、Executors.newSingleThreadExecutor():这种方式创建的线程池我们进源码看下

核心线程数1:核心线程数和最大线程数固定始终是1

最大线程数1:核心线程数和最大线程数固定始终是1

非核心线程数存活时间0:

非核心线程数存活时间单位毫秒:表示创建出来的线程只要不被使用,就立马销毁

工作队列LinkedBlockingQueue链表队列:上面我们已经看过LinkedBlockingQueue源码中的构造了,即int类型最大值21亿+

总结:newSingleThreadExecutor这个方式创建出来的线程,源码如名single,所以源码中只会创建1个线程,自始至终都是这1个线程在执行任务,后续任务到来都会被放到一个21亿+的队列中等待,没有非核心线程的概念,也就没有非核心线程的存活时间(所以源码中存活时间是0)

缺点:如果任务一直源源不断到来,队列中将一直存储,队列占用内存,同样会导致内存溢出oom

4、Executors.newScheduledThreadPool不甚了解,待日后研究补充

四、谈谈对线程池使用时,具体应该使用哪种线程池和怎样设置参数的理解:

        这个问题其实本人暂时并未有答案,个人感觉丝毫使用弹性很大,举几个例子:

1、在xxAPP中,线程池源码用的是Executors.newCachedThreadPool(),也就是如果也源源不断的任务通过线程池过来,就可以创建接近无数个线程(21亿+)同时工作,每个任务创建一个线程。这种方法从理论上分析会造成cpu100%,但实际该APP已经诞生n多年了,底层源码一直这样写,好像也没遇到什么问题。

2、在xxAPP中,线程池源码是这样写的new ThreadPoolExecutor(4, 4, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>());通过上面我们的解释,这样写线程池始终只会创建4个线程,其他任务到来将一直在一个接近无限大(21亿+)的队列中等待。这种写法理论上会造成内存溢出oom,但实际项目运行也已n年,似乎也没遇到什么这方面的问题。

3、在xxAPP中,线程池源码是这样写的new ThreadPoolExecutor(5, 5, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>());这样写线程池和第2种类似。理论上会造成内存溢出oom,但实际项目运行也已n年,似乎也没遇到什么这方面的问题。

总结:这已经超出本人理解范围,通过这几个项目的实际源码来看,似乎线程池的各种使用并未有太大影响(当然不能极端就写成1个线程执行任务)。

就总结到这里吧,有问题再补充。以后要常写文章,常总结,加油自己!

相关文章

  • 带你从源码分析AsyncTask

    前言 AsyncTask的分析会用到线程池的相关知识,如果没有了解过,可以先看一下我的笔记:线程池笔记 基础 As...

  • Java并发之JDK并发包(2)

    实战Java高并发程序设计笔记 线程复用:线程池 线程池可以有效的控制系统中线程的数据,可以避免线程不断的创建和关...

  • 线程池常见方法解读

    对于记性不好的同学写笔记很重要,有问题欢迎大家留言交流,sam出品。 线程池 用线程池解决的问题: 创建/销毁线程...

  • 线程笔记四(线程池、Lambda表达式)

    线程笔记四(线程池、Lambda表达式) 线程思想: ​ 我们使用线程的时候就去创建一个线程,这样实现起来非...

  • 线程池笔记

    1. 线程池 基本功能:线程的复用,减少创建和销毁线程的开销 当系统接收到一个任务时,需要一个线程,并不会立刻去创...

  • 线程池笔记

    线程池的好处是: 减避免因为线程频繁创建与销毁带来的开销 能有效控制线程池的最大并发数,避免大量的进程因为互相抢占...

  • 线程池笔记

    ThreadPoolExecutor 线程池的实现类是ThreadPoolExecutor类,完整的构造方法有7个...

  • 线程池笔记

    前言:线程池相关的所有类都在java.util.concurrent包下面 一、线程池的继承关系: 二、重点介绍T...

  • java线程池

    线程VS线程池 普通线程使用 创建线程池 执行任务 执行完毕,释放线程对象 线程池 创建线程池 拿线程池线程去执行...

  • 线程池

    最近项目组组织学习线程池,也受到了打击,以后坚持做笔记不偷懒了。 要了解线程池先了解几个相关的概念 线程:进程中负...

网友评论

      本文标题:线程池笔记

      本文链接:https://www.haomeiwen.com/subject/iubimrtx.html