笔记,读《Java并发编程艺术》的笔记,里面夹杂了一些自己的理解,可能不是很准确,建议阅读原著。
合理使用线程池的好处
- 降低资源消耗。降低了频繁创建和销毁造成的消耗。
- 提高响应速度。不必等待线程创建,而是立即执行。
- 提高线程的可管理性。
线程池的实现原理
首先判断核心线程池
里是否都在执行任务。如果不是,就创建一个新的线程来执行任务;如果是,就将任务添加到队列
中;如果队列满了,就在线程池
中 创建一个新的线程来执行任务;如果线程池
也满了,就交给饱和策略
来处理任务。
从上文中,我们知道线程池里面有一个小的线程池称为核心线程池
。任务先交给核心线程池
来处理。
工作线程
线程池创建线程时,会将线程封装成工作线程 Worker,Worker在执行完任务后,还会循环获取工作队列里的任务来执行。
线程池的创建
new ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
-
corePoolSize
: 核心线程池的大小。只要提交一个任务到线程池中,就创建一个新的线程,不管线程池中的其他线程是否空闲,直到线程池的线程数量大于corePoolSize
就不在创建。 -
maximumPoolSize
: 真正的线程池大小。允许创建的最大线程数。只有 队列满了的时候,线程池才会创建新线程。 -
keepAliveTime
: 如果线程处于休闲后,保持的存活时间。 -
unit
: 线程活动保持时间的单位。 workQueue
: 任务队列,用于保存等待执行任务的阻塞队列
。有四个可以选择:
-
ArrayBlockingQueue
: 基于数组结构的有界阻塞队列。 LinkedBlockingQueue
: 基于 链表结构 的阻塞队列。
-
Executors.newFixedThreadPool()
和Executors.newSingleThreadExecutor()
都是使用的此队列。
SynchronousQueue
: 不存储元素的阻塞队列。每插入一个元素就会阻塞等待另一个线程移除。
-
Executors.newCacheThreadPool()
使用的这个队列。
-
PriorityBlockingQueue
:一个具有优先级的无限阻塞队列。
-
threadFactory
: 用于设置创建线程的工厂,可以为每个线程设置更有意义的名字。 handler
: 设置饱和策略
,线程池满了的处理方式。有四种:
-
AbortPolicy
:直接抛出异常。 -
CallerRunsPolicy
:只用调用者所在的线程 来执行任务。 -
DiscardOldestPolicy
:丢弃队列里最近的一个任务,并执行当前任务。 -
DiscardPolicy
:不处理,丢弃掉。
execute()
和 submit()
方法
- 都是用来提交任务的。
-
execute()
没有返回值,不能够判断任务是否被线程池执行成功。 -
submit()
有返回值,返回一个 Future 对象来获取返回值。调用get()
方法会阻塞当前线程直到任务完成。
关闭线程池
通过调用shutdown
或 shutdownNow
方法来关闭线程池。
原理: 遍历线程池中的所有线程,并调用 interrupt
方法中断线程。无法响应中断任务可能永远无法终止,如被阻塞,某些线程可能会不理会 中断。
-
shutdown
:将线程池设置成 SHUTDOWN 状态,然后中断没有正在执行任务的线程,等待线程都指定完毕就关闭线程池。 -
shutdownNow
:会将线程池状态设置为 STOP 状态,将尝试停止所有正在执行或暂停任务的线程 。
监控线程池
监控线程池时,使用的一些属性:
- taskCount:线程池需要执行的任务数量。
- completedTaskCount:线程池在运行过程中已完成的任务数量。
- largestPoolSize:线程池曾经创建过的最大线程数量。
- getPoolSize:线程池的线程数量。
- getActiveCount:活动的线程数。