直接上图
1. 线程池是干什么的 :
线程池是利用已有的线程处理任务, 避免频繁创建线程浪费cpu性能。这里不得不提一嘴,将线程和任务分开的设计方案在多线程里面是十分好用的设计。
2.设计概述:
ThreadPool:线程池,存放线程的地方
阻塞队列 :存放任务的地方 ,此处的设计是利用队尾队列,将放入任务,对头出任务,让线程池中拿出一个线程处理任务。利用队列的设计可以满足先来的任务先执行。
具有取出任务 ,加入任务的功能
3.代码实现
(1)阻塞队列
属性分析:
1.dqueque:队列,里面可以真正的存放任务的地方
2.lock:ReentrantLock锁,这种锁的好处是可以,设计不同的等待条件,可以锁住和释放对于不同等待条件的线程。就把他看做一个锁就行 。
3.fullWaitSet:一种锁 , 线程增加任务的时候发现 ,任务队列满了,便锁住线程也可以设置超时等待。
4.emptyWaitSet:同上 ,取出任务的时候,针对队伍为空
5.capacity:队伍容纳量
方法分析:
1.ThreadPool:构造方法 ,可以设置队伍容纳量
2.take: 让线程从任务队列取出任务。
public T take(){
//加锁,避免多个线程同时取一个任务
lock.lock();
try{
//队列为空,线程进入等待
while (deque.isEmpty()){
try {
//线程进入 等待
emptyWaitSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//队伍不为空,移除队头元素
T task = deque.removeFirst();
//唤醒 所有因为队列为满而不能添加元素的线程
fullWaitSet.signalAll();
return task ;
}finally {
//锁释放
lock.unlock();
}
}
3.pull:take方法的改进 增加超时等待功能。
设置超时等待:
1.当任务数量过多,线程将多余的任务放入任务队列
2.当任务队列为空,自己也把任务执行完了,线程帮队列释放该任务
//超时等待的方法
public T pull(long timeOut, TimeUnit unit){
lock.lock();
try{
//将时间转化为纳秒
long waitTime = unit.toNanos(timeOut);
//队列为空
while (deque.isEmpty()){
try {
//超时
if (waitTime <= 0){
return null ;
}
//awaitNanos方法会自动减少timeOut,并返回还需要等待的时间
waitTime = emptyWaitSet.awaitNanos(timeOut);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//移除对头元素
T task = deque.removeFirst();
fullWaitSet.signalAll();
return task ;
}finally {
lock.unlock();
}
}
4.put:放入任务的方法:其实也可以设计超时等待
public void put(T task){
lock.lock();
//队列为空
try{
while (deque.size() >= capacity){
try {
//队列满了,让 想在队伍放入任务的 线程等一下
fullWaitSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//not
deque.addLast(task);
emptyWaitSet.signalAll();
}finally {
lock.unlock();
}
}
5.size:返回队伍任务数量,也需要加锁,有可能造成关于 读 的并发问题
线程池:
属性分析
1.taskQuue:阻塞队列, 线程需要从任务队列拿和放入任务
2.workers:可以执行的任务对象 (属于Work类的哈希set)
3.coreSize :线程数量
4.timeOut:超时等待时间
方法分析
execute(主要方法):1.线程将方法放入队列 2.执行任务 加锁避免并发造成多个任务往同一个地方加入任务,其实
//执行任务
public void execute(Runnable task){
//任务数量没有超过线程数量 ,交给 work执行
//else
//任务数超过coreSize的时候 加入任务队列暂存
synchronized (workers){
if (workers.size() < coreSize){
Worker worker = new Worker(task);
log.debug("新增work",worker);
worker.start();
workers.add(worker) ;
}else {
log.debug("加入任务队列",task);
taskQuue.put(task);
}
}
}
work类:是任务类继承Thead方法, 实现run接口 ,运行run接口就是一个线程了。
测试类
public class Test01 {
public static void main(String[] args) {
ThreadPool pool = new ThreadPool(2,1000,TimeUnit.MILLISECONDS,10);
for (int i = 0; i < 3; i++) {
int j = i;
pool.execute(()->{
log.debug("正在执行任务...{}",j);
});
}
}
}
pool:最多两个线程
for: 搞了三个任务
测试结果
后面还可以采取策略模式对 队列新增任务 进行改进,松一下耦合度