线程池ThreadPoolExecutor

概念

线程池是创建一定数量的线程,等待任务的到来,在执行完任务之后,继续放在池子中继续等待。

优点

  • 降低资源的消耗,节省线程创建和销毁的消耗。
  • 提高响应速度,节省创建和销毁的时间
  • 方便进行线程管理

简易线程池实现

这里实现了个非常简单的线程池,功能非常简单,仅仅为了方便理解线程池。

主要思路就是:1、建立一个线程池子,可以存放工作线程。

2、建立一个队列,可以存放需要运行的线程。

public class MyThreadPool {

  /**
   * 核心线程数
   */
  private static int CORE_POOL_SIZE = 5;
  /**
   * 队列最大容量
   */
  private static int MAX_QUEUE_SIZE = 100;
  /**
   * 队列
   */
  private BlockingQueue<Runnable> taskQueue;
  /**
   * 线程数组
   */
  private WorkThread[] workThreads;
  /**
   * 用户在构造这个池,希望的启动的线程数
   */
  private int worker_num;

  /**
   * 构造函数,使用默认参数
   * @param taskQueue
   */
  public MyThreadPool(BlockingQueue<Runnable> taskQueue) {
    this(CORE_POOL_SIZE, MAX_QUEUE_SIZE);
  }

  public MyThreadPool(int worker_num, int taskCount) {
    this.worker_num = worker_num;
    taskQueue = new ArrayBlockingQueue<>(taskCount);
    workThreads = new WorkThread[worker_num];
    for (int i = 0; i < worker_num; i++) {
      workThreads[i] = new WorkThread();
      workThreads[i].start();
    }
  }

  /**
   * 执行线程,其实就是将任务放入到队列中
   * @param runnable
   */
  public void execute(Runnable runnable) {
    try {
      taskQueue.put(runnable);
    } catch (InterruptedException e) {
      System.out.println("执行任务异常");
      e.printStackTrace();
    }
  }
  /**
   * 销毁线程池
   */
  public void destroy() {
    for (int i = 0; i < workThreads.length; i++) {
      WorkThread workThread = workThreads[i];
      workThread.stopTask();
      workThreads[i] = null;
    }
    taskQueue.clear();
  }

  /**
   * 自定义工作线程
   * 它的作用就是从队列中获取线程去执行以及停止任务
   */
  class WorkThread extends Thread {

    @Override
    public void run() {
      try {
        while (!isInterrupted()) {
          // 从队列中获取任务执行
          Runnable task = taskQueue.take();
          task.run();
          System.out.println(getId()+" 执行 :"+task);
        }
      } catch (InterruptedException e) {
        System.out.println("任务执行异常");
        e.printStackTrace();
      }
    }

    public void stopTask() {
      interrupt();
    }
  }
}
// 测试类
public class MyThreadPoolTest {

  public static void main(String[] args) throws InterruptedException {
    MyThreadPool myThreadPool = new MyThreadPool(3, 10);
    myThreadPool.execute(new WorkRunner("线程1"));
    myThreadPool.execute(new WorkRunner("线程2"));
    myThreadPool.execute(new WorkRunner("线程3"));
    myThreadPool.execute(new WorkRunner("线程4"));
    myThreadPool.execute(new WorkRunner("线程5"));
    myThreadPool.execute(new WorkRunner("线程6"));
    Thread.sleep(20000);
    myThreadPool.destroy();
  }


 static class WorkRunner implements Runnable {

    private String name;

    public WorkRunner(String name) {
      this.name = name;
    }

    @Override
    public void run() {
      try {
        Thread.sleep(2000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println(name + "任务执行完成");
    }
  }

}

 测试结果

 

  • 预定义的线程池

  • FixedThreadPool

创建固定线程数量的线程池。使用了无界队列.

// 可以看到最大线程数和核心线程数相同    
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
  • SingleThreadExecutor

创建单个线程。任务会顺序执行,使用了无界队列。

// 核心线程和最大线程都是1    
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
  • CachedThreadPool

每来一个任务就会创建一个线程,适合很多短期异步任务,使用SynchronousQueue。SynchronousQueue是一个内部只能包含一个元素的队列。插入元素到队列的线程被阻塞,直到另一个线程从队列中获取了队列中存储的元素。

// 核心线程是0,最大线程数是最大    
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
  • ScheduledThreadPoolExecutor
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

    public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }

需要定期执行周期任务。

newSingleThreadScheduledExecutor:只包含一个线程,只需要单个线程执行周期任务,保证顺序的执行各个任务
newScheduledThreadPool 可以包含多个线程的,线程执行周期任务,适度控制后台线程数量的时候
方法说明:
schedule:只执行一次,任务还可以延时执行
scheduleAtFixedRate:提交固定时间间隔的任务
scheduleWithFixedDelay:提交固定延时间隔执行的任务

    /**
       * 当执行时间大于间隔时间的情况。执行结果是
     * ....begin:2019-08-02 23:40:03
     * ....end:2019-08-02 23:40:11
     * ....begin:2019-08-02 23:40:11
     * ....end:2019-08-02 23:40:13
     * ....begin:2019-08-02 23:40:15
     * ....end:2019-08-02 23:40:20
     */
public static SimpleDateFormat formater = new SimpleDateFormat(
            "yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) {
        ScheduledThreadPoolExecutor schedule =
                new ScheduledThreadPoolExecutor(1);
        // initialDelay 初始化延迟 period间隔
        schedule.scheduleAtFixedRate(new Demo(), 1000, 6000,
                TimeUnit.MILLISECONDS);
    }

    static class Demo implements Runnable {
        AtomicInteger atomicInteger = new AtomicInteger(0);
        @Override
        public void run() {
            try {
                if (atomicInteger.get() == 0) {
                    System.out.println("....begin:"+formater.format(new Date()));
                    Thread.sleep(8000);
                    atomicInteger.getAndIncrement();
                    System.out.println("....end:"+formater.format(new Date()));
                } else if (atomicInteger.get() == 1) {
                    System.out.println("....begin:"+formater.format(new Date()));
                    Thread.sleep(2000);
                    atomicInteger.getAndIncrement();
                    System.out.println("....end:"+formater.format(new Date()));
                } else if (atomicInteger.get() == 2) {
                    System.out.println("....begin:"+formater.format(new Date()));
                    Thread.sleep(5000);
                    atomicInteger.getAndIncrement();
                    System.out.println("....end:"+formater.format(new Date()));
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
  • WorkStealingPool

jdk7以后新出的线程池。

 

  • 源码解析

  • 构造函数

    如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务;

    如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;

    如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;

    如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。


	* corePoolSize: 线程池核心线程数
	* 
maximumPoolSize:线程池最大数
	* 
keepAliveTime: 空闲线程存活时间。
	* 
unit: 时间单位
	* 
workQueue: 线程池所使用的缓冲队列
	* 
threadFactory:线程池创建线程使用的工厂,给新建的线程赋予名字
	* 
handler: 线程池对拒绝任务的处理策略

    AbortPolicy :直接抛出异常,默认;
    CallerRunsPolicy:用调用者所在的线程来执行任务
    DiscardOldestPolicy:丢弃阻塞队列里最老的任务,队列里最靠前的任务
    DiscardPolicy :当前任务直接丢弃
    实现自己的饱和策略,实现RejectedExecutionHandler接口即可

public ThreadPoolExecutor(int corePoolSize,
                       int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                  ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {

}

 

  • 全局变量

RUNNING:运行状态,该状态下线程池可以接受新的任务,也可以处理阻塞队列中的任务
SHUTDOWN:待关闭状态,不再接受新的任务,继续处理阻塞队列中的任务,当阻塞队列中的任务为空,并且工作线程数为0时,进入 TIDYING 状态
STOP:停止状态,不接收新任务,也不处理阻塞队列中的任务,并且会尝试结束执行中的任务,当工作线程数为0时,进入 TIDYING 状态
TIDYING:整理状态,此时任务都已经执行完毕,并且也没有工作线程,执行terminated方法后进入 TERMINATED 状态
TERMINATED:终止状态,此时线程池完全终止了,并完成了所有资源的释放

    // 线程池状态(前三位)及线程个数(后二十九位)
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    // 线程容量 000 11111111111111111111111111111
    private static final int CAPACITY = (1 << COUNT_BITS) - 1;

    // 运行中 111 00000000000000000000000000000
    private static final int RUNNING = -1 << COUNT_BITS;
    // 关闭  000 00000000000000000000000000000
    private static final int SHUTDOWN = 0 << COUNT_BITS;
    // 停止 001 00000000000000000000000000000
    private static final int STOP = 1 << COUNT_BITS;
    // 整理 010 00000000000000000000000000000
    private static final int TIDYING = 2 << COUNT_BITS;
    // 终止 011 00000000000000000000000000000
    private static final int TERMINATED = 3 << COUNT_BITS;

    // 线程池状态(前三位)
    private static int runStateOf(int c) {
        return c & ~CAPACITY;
    }

    // 线程个数(后29位)
    private static int workerCountOf(int c) {
        return c & CAPACITY;
    }

    private static int ctlOf(int rs, int wc) {
        return rs | wc;
    }
  • submit

运行线程,有返回值。可以看到它其实调的也是execute方法

    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }
  • excute

运行线程,无返回值

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        // 如果线程数量小于核心线程数
        if (workerCountOf(c) < corePoolSize) {
            // 调用addWorker创建新线程并运行,成功直接返回
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // 如果线程池在运行中并且成功将线程放到工作队列中
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            // 再次检查线程池状态,如果不在运行中,那么将线程从队列中移除
            if (!isRunning(recheck) && remove(command))
                // 执行拒绝策略
                reject(command);
            else if (workerCountOf(recheck) == 0)
                // 如果线程数为0,则添加一个新的线程
                addWorker(null, false);
        }
        // 如果新增新线程失败,则执行拒绝策略
        else if (!addWorker(command, false))
            reject(command);
    }

addWorker创建新线程并运行

   private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (; ; ) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // 如果线程池状态为SHUTDOWN、STOP、TIDYING、TERMINATED
            // 或者线程为空、阻塞队列为空则都返回false
            if (rs >= SHUTDOWN &&
                    !(rs == SHUTDOWN &&
                            firstTask == null &&
                            !workQueue.isEmpty()))
                return false;

            for (; ; ) {
                int wc = workerCountOf(c);
                // 如果线程数量超过最大线程数或者大于核心线程数(自定义最大线程数)返回false
                if (wc >= CAPACITY ||
                        wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                // 增加线程数
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();
                // 如果线程池状态发生改变,则跳出内循环,重新判断线程池状态
                if (runStateOf(c) != rs)
                    continue retry;
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        ThreadPoolExecutor.Worker w = null;
        try {
            w = new ThreadPoolExecutor.Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                // 加锁
                mainLock.lock();
                try {
                    int rs = runStateOf(ctl.get());
                    // 如果线程状态为运行中或者(状态为SHUTDOWN并且任务不为空)
                    if (rs < SHUTDOWN ||
                            (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive())
                            throw new IllegalThreadStateException();
                        // 添加到工作线程集合中
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                // 如果word添加成功,则启动线程
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            // 线程没有启动成功,则将word从集合中移除,并且size-1
            if (!workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

Worker里run方法所调用的方法

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock();
        boolean completedAbruptly = true;
        try {
            // 如果线程不为空,并且从队列中成功获取到任务
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // 当线程池是处于STOP状态或者TIDYING、TERMINATED状态时,设置当前线程处于中断状态
                // 如果不是,当前线程就处于RUNNING或者SHUTDOWN状态,确保当前线程不处于中断状态
                // 重新检查当前线程池的状态是否大于等于STOP状态
                if ((runStateAtLeast(ctl.get(), STOP) ||
                        (Thread.interrupted() &&
                                runStateAtLeast(ctl.get(), STOP))) &&
                        !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        // 运行任务
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x;
                        throw x;
                    } catch (Error x) {
                        thrown = x;
                        throw x;
                    } catch (Throwable x) {
                        thrown = x;
                        throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    // 已完成任务数+1,解锁
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            // 做一些售后工作,例如将线程从集合中移除、判断线程是否需要终止
            // 判断队列是否为空,是否需要保留至少一个线程等工作
            processWorkerExit(w, completedAbruptly);
        }
    }

getTask从队列中获取任务

    private Runnable getTask() {
        boolean timedOut = false;

        for (; ; ) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // 如果线程状态非运行状态并且(状态是停止、整理、终止或者队列为空)
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                // 将线程数 -1
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            // 如果线程数量大于自定义最大线程数 并且 (线程数量>1或者阻塞队列为空),返回null
            if ((wc > maximumPoolSize || (timed && timedOut))
                    && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                // 从队列中获取任务
                Runnable r = timed ?
                        workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                        workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }
  • 关闭线程池

  • shutdown

不再接收新的任务,只会中断所有没有执行任务的线程

    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            advanceRunState(SHUTDOWN);
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
    }
  • shutdownNow

不再接收新的任务,还会尝试停止正在运行或者暂停任务的线程

    public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            advanceRunState(STOP);
            interruptWorkers();
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
    }

可以看到shutdown是将状态改为SHUTDOWN,而shutdownNow是将状态改为STOP,再返回去看getTask这个方法的这句,可以看到是不会拿到新的任务去运行了。

            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                // 将线程数 -1
                decrementWorkerCount();
                return null;
            }

再看runWorker这个方法的这句。可以看到当是STOP及以上状态时候,会让线程中断,而SHUTDOWN则不会。

                if ((runStateAtLeast(ctl.get(), STOP) ||
                        (Thread.interrupted() &&
                                runStateAtLeast(ctl.get(), STOP))) &&
                        !wt.isInterrupted())
                    wt.interrupt();

计算密集型和IO密集型

  • 计算密集型。指大部分时间都花费在了计算上,消耗cpu资源。
  • IO密集型。指大部分时间花在网络、磁盘等交互上。

 合理配置线程池

  • 计算密集型线程任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低。所以线程数适当小一点,最大推荐:机器的Cpu核心数+1
  • IO密集型。任务的大部分时间都在等待IO操作完成。对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。所以推荐线程数是机器的Cpu核心数*2

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值