线程池学习之线程池的工作过程

看一下线程池的核心类ThreadPoolExecutor中的构造器

线程池学习之线程池的工作过程_第1张图片

它有七个参数

int corePoolSize:
这个参数是线程池核心工作线程,就像去银行一样,他所有的窗口不一定都开。假设银行总共有6个窗口,开了三个,这3个就是我们的核心线程数即corePoolSize。

int maximumPoolSize:
这个参数是最大线程数的意思。就像上面的例子说的银行总共开了6个窗口,这6个窗口就是线程池最大承担同时工作的线程的个数。
就是最多可以同时有多少个窗口同时工作。
BlockingQueue workQueue:
这个参数是阻塞队列,当我们在银行班业务时,往往不是所有的窗口都是开的,一般只会开一部分,人多了的话就会进入银行的等待区,线程池也是这么设计,这个阻塞队列就是用来存储因为线程工作空间被占用,而只能等待在等候区的线程。但是当我们的等候区的线程也满了的时候,有工作任务再次被丢进来了,线程池会再次申请开新的线程,就像银行候客区满的时候,银行为了提高工作效率,会增加窗口,这时候打开所有的窗口,及线程池工作线程达到极限,后面的线程会进入阻塞队列。 RejectedExecutionHandler handler:
这个是线程池的拒绝策略,当线程池已经达到最大极限,并且队列里面也已经满了,就像银行一样,所有窗口都开了,整个银行里面都是人,为了维护银行的安全,当然需要制定一定的策略处理这种线程非常多的情况,对于拒绝策略,这里暂时不做介绍。
long keepAliveTime, 多余的空闲线程的存活时间。就像我们的银行一样,由于人非常多我们把剩下的所有窗口都开了,那么如果我们业务已经处理完了,在非核心线程和队列里面的任务都已经处理完了,那么这个时候这个参数就会有作用了,设置一段时间,如果做完了队列和非核心线程的任务,在这个时间段内没有任务,那么后来新加的窗口,鸡我们的非核心线程数机会慢慢的关闭,直到只剩核心线程数。

TimeUnit unit:
这个参数代表这上面非空闲线程存活时间的单位。

ThreadFactory threadFactory:

表示生成线程池中工作线程的工厂,用于创建线程,一般用默认的即可。

下面有一段测试的代码

package juc.threadpool;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Description
 * @Author DJZ-WWS
 * @Date 2019/2/26 16:39
 */
public class ThreadPoolTest {
    public static void main(String[] args) throws InterruptedException {
     
        ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 3000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.DiscardOldestPolicy());

        Runnable myRunnable = () -> {
            try {
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + "run");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        executor.execute(myRunnable);
        executor.execute(myRunnable);
        executor.execute(myRunnable);
        System.out.println("---先开启三个线程---");
        System.out.println("核心线程数" + executor.getCorePoolSize());
        System.out.println("线程池线程数" + executor.getPoolSize());
        System.out.println("队列任务数" + executor.getQueue().size());
        executor.execute(myRunnable);
        executor.execute(myRunnable);
        executor.execute(myRunnable);
        System.out.println("---再开启三个线程---");
        System.out.println("核心线程数" + executor.getCorePoolSize());
        System.out.println("线程池线程程数" + executor.getPoolSize());
        System.out.println("队列任务数" + executor.getQueue().size());
        Thread.sleep(8000);
        System.out.println("----8秒之后----");
        System.out.println("核心线程数" + executor.getCorePoolSize());
        System.out.println("线程池线程线程池数" + executor.getPoolSize());
        System.out.println("队列任务数" + executor.getQueue().size());
        executor.shutdown();
    }

}

结果如下:

线程池学习之线程池的工作过程_第2张图片

简单的说明:

设置线程核心设置是3个,最大线程数6个

当我们一开我们往线程池里丢了三个任务,每个线程会工作2秒,再线程还没有工作完的时候我们又丢进3个线程这个时候线程会进入阻塞队列,因为队列是空的,所以不会创建新的线程,这时候线程池里面的线程情况是核心线程数3个队列任务个数3个线程池里的线程总数是3个。当过了一段时间之后,所有任务都完成了线程池里面只剩核心线程再工作,所以队列任务数0,核心线程数3个。

这就是线程池的整体工作过程。

下图是线程池的工作原理图

线程池学习之线程池的工作过程_第3张图片

  ThreadPoolExecutor中线程执行任务的示意图如下图所示

线程池学习之线程池的工作过程_第4张图片

1.在execute()方法中创建一个线程,让这个线程执行任务

2.这个线程执行完上图中的任务后,会反复从BlockQueue获取任务来执行。

线程池execute方法源码实现如下:

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
      //如果线程数小于基本线程数,则创建线程并执行当前任务
        if (workerCountOf(c) < corePoolSize) {
            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)
                addWorker(null, false);
        }
//如果县城内池不处于运行或者任务无法放入队列,并且当前线程数小于最大允许的线程数量,
//则创建一个线程执行任务
        else if (!addWorker(command, false))
//抛出RejectedExecutionException
            reject(command);
    }

你可能感兴趣的:(JUC)