JUC之线程池
解决啥问题
要实现多线程,就要实现Runnable、或者继承Thread,重写run方法并且调用start来启动线程,完了还要销毁,频繁的创建销毁浪费资源,所以就先跑几个线程,让有限的线程来做多个线程的run。
思路
定义线程池
- coreSize 核心线程个数:只有coreSize个的线程在跑
- maxSize 最大线程个数:初始化BlockingDeque时使用,调用offer方法时,如果超过这个数量就无法再将线程加入线程池
- BlockingDeque<Runnable/> queue:存储传进来的线程
- 运行状态标识位:控制线程池是否关闭
- 内部类 Worker:线程池中自己的线程,就是Work的run方法来实现传入线程的run方法。由于传入的参数是Runnable所以要调run方法,完全可以是其他自定义的类但是实际执行的方法必须统一。
核心方法
- 构造函数:接受coreSize、maxSize,初始化maxSize长度的BlockingDeque、创建coreSize个数的Worker
- 增加线程:往队列里丢线程,这里用offer方法,如果超出长度,会阻塞。
- 停止线程:修改标志位
测试一把
定义一个实际的线程来模拟
三个(coreSize)核心工作线程,最多能屯十个(maxSize)线程的活,这里我们就刚好跑完十个线程的活
代码
package com.juc; import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
/**
* 我的线程池 */public class MyThreadPool {
/**
* 内部维护的队列 */
private BlockingDeque<Runnable> queue;
private boolean isRunning = true;
public MyThreadPool(int coreSize,int maxSize) {
this.queue = new LinkedBlockingDeque<Runnable>(maxSize);
System.out.println("正在创建线程池,核心线程数为" + coreSize);
for (int i = 0; i < coreSize; i++) {
new Thread(new Worker()).start();
}
}
public void addThread(Runnable r,int i) {
queue.offer(r);
}
public void shutDown() {
isRunning = false;
System.out.println("线程池正在关闭。。");
}
class Worker implements Runnable {
@Override
public void run() {
System.out.println("工作线程" + Thread.currentThread().getName() + "正在运行");
while (isRunning||!queue.isEmpty()) {
Runnable thread = queue.poll();
if (thread != null) {
thread.run();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
MyThreadPool myThreadPool = new MyThreadPool(3,10);
for (int i = 1; i <= 10; i++) {
myThreadPool.addThread(new ActualThread(i),i);
}
myThreadPool.shutDown();
}
}
class ActualThread extends Thread {
private int i;
public ActualThread(int i) {
super();
this.i = i;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "跑第" + i + "个线程任务");
}
}
结果
优化
worker线程的创建时机
当线程池中已实例化完成并创建了三个,但是如果加入的线程只有一个或者根本没有,那么这三个Worker不就白跑了?所以Worker的创建需要由实际线程add进来才触发,有可能一个Worker线程就够了。
拒绝策略
如果需要跑的线程大于maxSize,则需要拒绝加入队列。BlockingDeque的offer方法若是超过数量,则添加失败,return false。如果刚好过几秒queue中的线程已经跑完了,那当前的线程就能添加成功,所以添加线程方法可以使用 offerLast(E e, long timeout, TimeUnit unit) 方法给个缓冲时间。
/** * @throws NullPointerException {@inheritDoc}
* @throws InterruptedException {@inheritDoc}
*/public boolean offerLast(E e, long timeout, TimeUnit unit)
throws InterruptedException {
if (e == null) throw new NullPointerException();
Node<E> node = new Node<E>(e);
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (!linkLast(node)) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
return true;
} finally {
lock.unlock();
}
}
JUC包的线程池
Executor源码分析
类图
阿里规约
阿里的《码出高效:JAVA开发手册》中建议不要直接用Executor调用静态方法来创建线程池,通过ThreadPoolExecutor来创建线程池,更明确线程池的运行规则,规避资源耗尽的风险。
以上是 JUC之线程池 的全部内容, 来源链接: utcz.com/z/513505.html