首页 > 技术文章 > 线程池

majingyun 2021-04-16 14:13 原文

线程池

一、线程池介绍

目的:为了避免系统频繁地创建和销毁,我们可以让创建的线程复用。

意义:在线程池中,总有那么几个活跃线程,当你需要使用线程时,可以从池子里随便拿一个空闲线程,当完成工作时,并不着急关闭线程,而是将这个线程退回到线程池中,方便他人使用。这样减少了频繁地创建和销毁线程,提高性能。

二、线程池工作原理

1、corePool 核心线程数

2、WorkQueue 阻塞队列,队列有多种实现,① 有长度限制的;(设置的多大就是多大)② 无长度限制的;(内存多大长度就有多大)③占位功能的(只能容纳一个),主要有以下几种:

① ArrayBlockingQueue,数组,有长度的,初始化后不能再改。

② LinkedBlockingQueue,它的内部以链表的形式对元素进行存储。可指定元素上限,否则上限为Integer.max_value

③ DelayQueue

它对元素持有直到一个特定的延迟到期。注意:进入其中的元素必须实现Delayed接口。

④ SynchronousQueue,占位,只能容纳单个元素

⑤ PriorityBlockingQueue

它是⼀个⽆界的并发队列。⽆法向这个队列中插⼊null值。所有插⼊到这个队列中的元素必须实现Comparable接⼝。因此该队列中元素的排序就取决于你⾃⼰的Comparable实现。

 

3、maxnumPoolSize 最大线程数

4、ThreadFactory 工厂模式创建线程

5、RejectedExecutionHandler 拒绝策略

① AbortPolicy  丢弃任务,并抛出RejectedExecutionException异常。(默认拒绝策略)

② DiscardPolicy 丢弃任务但是不抛出异常。

③ DiscardOldestPolicy 丢弃队列中最前面的任务,然后重新尝试任务。

④ CallerRunsPolicy 由调用线程处理该任务。

线程池的构造函数:ThreadPoolExecutor 

 

Executors提供四种线程池,分别为:

1newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无法收回,则创建新线程。

2)newFixedThreadPool

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

3)newScheduledThreadPool

创建⼀个定⻓线程池,⽀持定时及周期性任务执⾏。

4)newSingleThreadExecutor

创建⼀个单线程化的线程池,它只会⽤唯⼀的⼯作线程来执⾏任务,保证所有任务按照指定顺序(FIFO LIFO, 优先级)执⾏。

newCachedThreadPool 线程被消费是走的线程复用。

三、线程池源码解析

1、int c = ctl.get();

 ctl=[3位]线程池状态 + [29位]线程池中线程数量

private static int ctlOf(int rs, int wc) {
    return rs | wc; // 按位取或,即:同为0时为0,否则为1。此处可以理解为ctl内容的拼接
}

① runState rs 获取运行状态,在高三位

① WorkerCount wc  29位,表示获得当前活动的线程数,线程池在不考虑参数的情况下,最大值是229次方个线程。

2、Execute 方法

创建线程,根据核心线程数、队列、最大线程数。

3、AddWorker(Runnable firstTask,boolean core)

Core = true 核心线程数

Core = false 最大线程数

步骤一:试图将workerCount+1

 步骤二:workerCount成功+1后,创建Worker,加入集合workers中,并启动Worker线程

1)retry使用

retry:

continue retry =break

break retry 跳出所有循环

2)worker里面承载中thread runnable

4、start方法

调用的是workerrun方法

 

 

5、线程复用

如果可以复用就复用,不可以复用就新建。

线程池里的线程分为两种,一种是空闲的;一种是忙碌的;当线程空闲时会去阻塞队列里拉取阻塞的线程去消费。

boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

getTask关键点:

* poll(time):取走BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,取不到时返回null
* take():取走BlockingQueue里排在首位的对象,BlockingQueue为空,阻断进入等待状态直到Blocking有新的对象被加入为止

Workqueue 里面存的是runnable

6newCachedThreadPool

关键是SynchronousQueue

Request date

SynchronousQueue也有公平和非公平,公平的话是Queue ;不公平是stackSynchronousQueue默认是非公平的,也就是stack(堆),堆是先进后出。Data 入栈时,如果为空,直接返回null,走第三步,在线程池创建线程,当线程空闲时,去队列拉取任务,相当于request,这时候有data再次请求入栈,与request配对,一起出栈,执行data里面的runnable,完成消费。

阻塞的是request,不是data,data如果在栈里面配对成功,就能消费,如果不成功,就在线程池建立线程。

 

 

 

推荐阅读