线程池原理之自定义线程池

一、队列

先说一下线程池底层的数据结构

队列:一种线性表,它的特性是先进先出,插入在一端,删除在另一端。

队列又分为阻塞队列BlockingQueue和非阻塞队列ConcurrentLinkedQueue

                                    

生产者生产元素插入队列,消费者消费元素在另一端删除(假定队列的容量为10)

它们两者的区别:

1、非阻塞队列:如果存放超出队列容量,则队列信息丢失;

      阻塞队列:如果存放超出队列容量,则进行等待(等待时间为队列设置的生产者等待超时时间)

2、非阻塞队列:获取队列元素时,若队列为空,则返回null

       阻塞队列:获取队列元素时,若队列为空,则进行等待(等待时间为队列设置的消费者等待超时时间)

阻塞队列在操作中若规定了超时时间,但是操作不需要等待时,就不用等待;若在规定的超时时间内没有完成操作,则放弃操作。若不规定超时时间,就可以认为是非阻塞队列。

其中线程池底层使用的就是阻塞队列

二、线程的状态

 下面是大家熟知的线程状态转换图:

                                           线程池原理之自定义线程池_第1张图片

三、线程池

线程池简单来说就是管理线程的,使用它的优点有以下三个大的方面:

1、降低资源消耗:重复利用机制降低了线程创建、销毁的资源消耗;

2、提高相应效率:调度线程时状态切换较快;

3、方便管理

3.1 线程池创建的几种方式

线程池原理之自定义线程池_第2张图片

四种线程池的创建方式,在底层使用的都是ThreadPoolExecutor的构造方法

1、可缓存线程池

线程池原理之自定义线程池_第3张图片

2、可固定长度线程池

线程池原理之自定义线程池_第4张图片

3、可定时线程池

4、单线程池

线程池原理之自定义线程池_第5张图片

3.2 线程池原理

                                   线程池原理之自定义线程池_第6张图片

简述一下:在线程池execute(Runnable runnable)的时候,判断一下已经创建的线程数量是否大于等于核心线程数,若大于等于,则直接创建线程并且调用start()方法;若已创建的线程数大于等于核心线程数了,就将新线程存放在阻塞队列中,等待之前运行的线程结束后,再抢占资源继续运行。注意:此处不再是新创建线程,而是重复利用之前已创建的线程。阻塞队列大小基本都是Integer.MAX_VALUE,一般不会满,如果队列已满,就判断已经创建的线程数量是否大于等于最大线程数,若不大于等于,则直接创建线新程执行。所以,只有在阻塞队列满时,才会用到最大线程数。

3.3  自定义线程池

上面也说了,所有的线程池都是使用ThreadPoolExecutor的构造方法,所以我们自己自定义线程池其实也就是使用ThreadPoolExecutor的构造方法。

1、先自定义一个线程

线程池原理之自定义线程池_第7张图片

2、自定义线程池

线程池原理之自定义线程池_第8张图片

3、分析运行结果

线程池原理之自定义线程池_第9张图片

从结果中也可以证实之前将的原理:我们定义了一个核心线程数为1、最大线程数为2、阻塞队列容量为3的线程池。

在下发线程任务一时:创建线程数为0,小于核心线程数,此时立即创建线程并执行;

在下发线程任务二时:创建线程数为1,不小于核心线程数,阻塞队列不满,将任务二存放在队列中;

在下发线程任务三、四时和任务二相同;

在下发线程任务五时:阻塞队列已满,创建线程数为1,小于最大线程数,此时立即创建线程并执行。

所以运行结果,先是任务一,再是任务五,而且任务五的线程是新创建的thread-2。而在队列中的任务二、三、四不再创建新的线程。

如果在原有代码基础上再下发任务六,程序就会报错。

因为下发的任务数已经超过了最大线程数2+阻塞队列容量3

你可能感兴趣的:(并发编程)