JUC之线程池

老子叫甜甜 提交于 2020-02-27 13:59:35

自己搞个线程池

看了mayikt之后很有feel,搞一波,顺便用好看的carbon插件来生成代码图片

解决啥问题

要实现多线程,就要实现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) {  
       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));  
        }  
        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源码分析

  • 构造函数 每个方法都有两种传参形式,实际上就是对方法的重载,效果相同
  1. newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。两种
public static ExecutorService newCachedThreadPool() {  
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,  
                                  60L, TimeUnit.SECONDS,  
                                  new SynchronousQueue<Runnable>());  
}
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {  
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,  
                                  60L, TimeUnit.SECONDS,  
                                  new SynchronousQueue<Runnable>(),  
                                  threadFactory);  
}
  1. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  2. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
  3. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

类图

阿里规约

阿里的《码出高效:JAVA开发手册》中建议不要直接用Executor调用静态方法来创建线程池,通过ThreadPoolExecutor来创建线程池,更明确线程池的运行规则,规避资源耗尽的风险。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!