自定义线程池
1. 线程池的作用
- 减少在创建和销毁线程上所花的时间以及系统资源的开销
- 如果不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存
2. 实现自定义的线程池
构建步骤
- 消息队列(任务队列)
- 线程池
- 测试
想法:
按照上面的构建步骤开始说: - 消息队列
先有一个容器,有两个方法,一个 put 一个take ,这个队列要多个线程访问,所以用锁 - 线程池
有一个线程集合,有一个 消息队列,还有一个线程的size(创建几个线程)。还有一个执行的方法。 - 线程池的线程
先执行完自己的任务,在看线程池中的消息队列还有没有消息。如果有就拿一个消息来执行行。直到消息队列为空,将自己从线程集合中移除
3. 代码
1.创建消息队列
/**
* 定义线程吃,首先是要定义阻塞队列
*/
@Slf4j
class BlockQueue<T> {
/**
* 存放消息的地方 双向链表, array比link性能好
*/
private Deque<T> deque = new ArrayDeque<T>();
/**
* 定义锁
*/
private ReentrantLock lock = new ReentrantLock();
/**
* 生产者 等待room
*/
private Condition fullWaitSet = lock.newCondition();
/**
* 消费者等待room
*/
private Condition emptyWaitSet = lock.newCondition();
/**
* 容量
*/
private int capcity;
public BlockQueue(int capcity) {
this.capcity = capcity;
}
/**
* 不带 等待时间的 take方法
* @return T
*/
public T take() {
/*
流程:
- 上锁,
- 看队列有没有
- 有返回 ,没有等待
- 唤醒
- 解锁
*/
lock.lock();
try {
log.debug("尝试take");
while (true == deque.isEmpty()) {
try {
log.debug("emptyWaitSet await");
emptyWaitSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("emptyWaitSet removefirst");
T result = deque.removeFirst();
fullWaitSet.signalAll();
return result;
} finally {
lock.unlock();
}
}
/**
* 带等待时间的 take方法
* @param timeOut 等待时间
* @param unit 等待时间单位
* @return 队列中第一个消息
*/
public T poll(long timeOut, TimeUnit unit) {
/*
流程:
- 上锁,
- 看队列有没有
- 有返回 ,没有等待
- 唤醒
- 解锁
*/
lock.lock();
try {
log.debug("尝试poll");
//变为统一的时间
long delayTime = unit.toNanos(timeOut);
while (true == deque.isEmpty()) {
try {
log.debug("emptyWaitSet await");
if (delayTime < 0) {
return null;
}
// 防止虚假唤醒。awaitNanos的返回值是 总等待时间 - 被唤醒的经历的时间
delayTime = emptyWaitSet.awaitNanos(delayTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("emptyWaitSet removefirst");
T result = deque.removeFirst();
fullWaitSet.signalAll();
return result;
} finally {
lock.unlock();
}
}
/**
* 放消息
* @return T
*/
public void put(T task) {
/*
流程
- 判断满不满
- 不满就放,满就等待
- 唤醒等待线程
*/
lock.lock();
try {
while (capcity == deque.size()) {
try {
log.debug("队列已经满,fullwaitset:{}", fullWaitSet);
fullWaitSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
deque.addLast(task);
emptyWaitSet.signalAll();
} finally {
lock.unlock();
}
}
}
2.线程池
/**
* 自定义线程池 首先要有blockQueue(任务队列),线程池线程数,线程集合,
* 线程池的作用就是创建线程并且执行任务
*/
@Slf4j
class ThreadPool {
/**
* 消息队列(任务队列)
*/
private BlockQueue<Runnable> taskQueue;
/**
* 工作线程数
*/
private int coreSize;
/**
* 请注意:这里的线程集合不是线程安全的,在消息队列为空的情况下,多个线程要从这个集合里面移除他。
*/
private HashSet<Work> works = new HashSet<>();
/**
* 等待时间
*/
private long awaitTime;
/**
* 等待时间单位
*/
private TimeUnit unit;
/**
* 初始化
*
* @param coreSize 工作线程数
* @param taskQueueSize 消息队列大小
* @param awaitTime 消费者等待时间
* @param unit 等待时间单位
*/
public ThreadPool(int coreSize, int taskQueueSize, long awaitTime, TimeUnit unit) {
this.coreSize = coreSize;
this.taskQueue = new BlockQueue<Runnable>(taskQueueSize);
this.awaitTime = awaitTime;
this.unit = unit;
}
/**
* 线程池 执行任务的方法
*
* @param task 需要执行的任务
*/
public void execute(Runnable task) {
/**
* 创建线程执行任务.
* 懒惰初始化;
* - 如果workset的数量小于coresize 那就创建,执行任务
* - 否则:任务进任务队列
* 这里要加锁:因为work是公用的,
*/
synchronized (works) {
if (works.size() < coreSize) {
//创建
Work work = new Work(task);
// 添加线程进works集合
works.add(work);
//开启线程
work.start();
} else {
//任务进消息队列
taskQueue.put(task);
}
}
}
/**
* 内部类 工作线程
*/
class Work extends Thread {
/**
* 任务
*/
private Runnable task;
public Work(Runnable task) {
this.task = task;
}
/**
* 重写run方法
*/
@Override
public void run() {
/*
这里的逻辑:
- 执行自己的task 执行完之后 看队列中还有没有任务,如果有继续执行
*/
//判断task是否为null,线程刚创建好的时候。task不是null,
while (task != null) {
try {
log.debug("线程正在运行");
task.run();
} finally {
task = null;
// 从消息队列中取一个,赋值为task。有最大的等待时间
task = taskQueue.poll(awaitTime, unit); //如果用take方法。在所有的任务执行完了之后,线程不会退出,一直在等待。。。
//修改方式:
// 加一个超时等待。
}
}
// 如果消息队列中没有消息,
synchronized (works) {
//从工作线程中移除 自己
works.remove(this);
log.debug("worker 被移除{}", this);
}
}
}
}
3.测试
public static void main(String[] args) {
ThreadPool threadPool = new ThreadPool(2, 1, 1, TimeUnit.SECONDS);
threadPool.execute(() -> {
try {
TimeUnit.SECONDS.sleep(1);
log.debug("1");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadPool.execute(() -> {
try {
TimeUnit.SECONDS.sleep(1);
log.debug("2");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadPool.execute(() -> {
log.debug("3");
});
}
结果:
这个功能没有完全: 后面会有增强版