线程池
关于线程池的基本概念在之前的这片文章中提到了,这里主要分享下在尝试实现简单的CachedThreadPool线程池过程中的一些思考以及代码。
await signal VS wait notify
在实现阻塞队列的时候,这是一种典型的生产者-消费者模式,需要用到线程的等待和唤醒。
第一种实现方式是利用Object的wait
和notify
来实现。
1 | final Object object = new Object(); |
第二种方式是使用Lock和Condition的await
和signal
来实现
1 | ReentrantLock lock = new ReentrantLock(); |
简单来说,这两种方式提供的功能是一样的,相比之下,Object的方式理解起来比较简单,Lock的方式提供了更强大的功能,比如
- Lock可以支持多种Condition,可以更有针对的进行notification,而Object只能single notification
- Lock的
await(long time, TimeUnit unit)
支持超时时间的设置
Worker线程的Task如何实现
创建出来的线程当任务完成之后便会Terminated,那要让线程池中的线程长久存在,就需要将Task实现成死循坏,让方法无法退出。但没有任务的时候,又不能占用CPU的时间。
所以就利用了阻塞队列。不断的从队列中取任务来执行,当没有任务的时候,让线程成为WAITING或者TIMED_WAITING状态,等待任务的到来,再唤醒线程,参考下面代码的workerTask()
方法。
KeepAlive如何实现
我们知道如果启用了KeepAlive的话,当线程数>核心线程数,并且空闲时间超过了KeepAlive的话,多余的线程便会被销毁。那线程池是如何判断线程空闲的时间超过了设定时间呢?
正是利用了Confition的超时等待方法await(long time, TimeUnit unit)
,当线程等待超时后,方法会返回false
如何动态的增加Worker线程
由于我这里实现的是CachedThreadPool, 当队列中的任务满了,并且Worker线程数小于最大线程数的时候,便会动态的增加Worker的线程数。
实现方式是利用队列的offer()
方法,如果队列满了,该方法便返回false
,再来获取当前Worker的线程数,如果小于最大数,便创建,反之则直接拒绝任务。
简单的实现
第一个文件是阻塞队列的实现,第二个文件是线程池的实现。
BlockingQueue.java
1 | package com.ndrlslz.core; |
ThreadPool.java
1 | package com.ndrlslz.core; |