Java线程池

什么是线程池? 

线程池:从它名字上来看,就是把一堆线程放进一个池子里面,来任务后取出池子里面的线程,工作完后放回池子。

为什么要使用这个?

首先要明白线程分为用户级线程和内核级别线程。我们使用new Thread()创建的线程其实底层是通过系统调用创建的内核级别线程。既然是内核级别线程,那么必然会创建一个TCB,TCB是消耗内存的。

所以

好处1:能够避免频繁的系统调用。系统调用对于任何一个操作系统开销都是很大的。

好处2:消耗内存,熟悉JVM的都知道,虚拟机栈、程序计算器是随着线程的生命周期的,自然而然,当并发量高的时候,这些东西也会创建很多,栈默认是1m,可以通过-xss参数来改变大小。

 

用法:

自定义线程池构造方法

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

 

一共有7个参数:

corePoolSize:核心线程数,这些线程是一直存在线程池中的

maximumPoolSize:最大线程数

keepAliveTime:存活时间,当超过核心线程线程后,但是小于最大线程数,这些线程如果没有任务,会在keepAliveTime时间后消亡。

unit:单位,keepAliveTime的单位。

workQueue:阻塞队列,这里规定了阻塞队列里面放的元素必须是Runnable,当核心线程都在工作时,这时候新来的任务就会被放进阻塞队列。

threadFactory:线程工厂。

我们看看threadFactory是啥,其实里面很简单,就是接口,里面有个方法来返回创建的后的线程,简单点说:线程池里面的线程都是通过下面的方法newThread()来创建的

public interface ThreadFactory {

    /**
     * Constructs a new {@code Thread}.  Implementations may also initialize
     * priority, name, daemon status, {@code ThreadGroup}, etc.
     *
     * @param r a runnable to be executed by new thread instance
     * @return constructed thread, or {@code null} if the request to
     *         create a thread is rejected
     */
    Thread newThread(Runnable r);
}

handler:拒绝策略。简单点说就是当你最大线程数的线程都在工作时,并且阻塞队列也满了,这个时候你怎么去处理新的任务。jdk提供了四种拒绝策略:

ThreadPoolExecutor.CallerRunsPolicy(): 抛弃旧的任务
ThreadPoolExecutor.DiscardPolicy() :抛弃当前的任务
ThreadPoolExecutor.AbortPolicy() :抛出java.util.concurrent.RejectedExecutionException异常,  Default异常
ThreadPoolExecutor.CallerRunsPolicy() :由创建了线程池的线程来执行被拒绝的任务

也可以自定义自己的拒绝策略,实现下面的接口就行

public interface RejectedExecutionHandler {

    /**
     * Method that may be invoked by a {@link ThreadPoolExecutor} when
     * {@link ThreadPoolExecutor#execute execute} cannot accept a
     * task.  This may occur when no more threads or queue slots are
     * available because their bounds would be exceeded, or upon
     * shutdown of the Executor.
     *
     * <p>In the absence of other alternatives, the method may throw
     * an unchecked {@link RejectedExecutionException}, which will be
     * propagated to the caller of {@code execute}.
     *
     * @param r the runnable task requested to be executed
     * @param executor the executor attempting to execute this task
     * @throws RejectedExecutionException if there is no remedy
     */
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

 

原理:

构造方法很简单,就不具体展开了

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        //参数校验
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        //暂时不知道是啥,不影响整体流程
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        //赋值
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

  关键方法:

  注意:ctl 可以理解是两个字段的打包,一个是线程池的状态,后面一个是workerCount,可以看到初始化时,是RUNNING和0;

 

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
 
public void execute(Runnable command) {
        //1.参数校验
        if (command == null)
            throw new NullPointerException();
        
        //获取ctl的值 
     int c = ctl.get();
        //通过workerCountOf(c)反过来获取workerCount,如果小于核心线程数
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //如果大于核心线程数,并且能够正常加入阻塞队列,注意这里用的是offer,不是add
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //线程池关闭了
            if (! isRunning(recheck) && remove(command))
                reject(command);
            //wc == 0,不知道这种是什么情况,暂时不用管
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //如果满了,尝试加入最大线程,可以看到后面参数为false,这里就表示为最大线程
        else if (!addWorker(command, false))
            //加入失败,执行拒绝策略。里面代码很简单
            reject(command);
    }
//这里core表示新加的线程是否是核心线程
private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            //通过runStateOf函数返过来获取线程池状态
            int rs = runStateOf(c);

            // 校验
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                //校验当前workerCount
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                //通过CAS则增加ctl的值,增加失败,就重试,这里解决多线程下并发增加ctl问题
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                //增加失败重新获取ctl值
                c = ctl.get();  // Re-read ctl
                //如果线程池状态发生改变
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            //创建Worker
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int rs = runStateOf(ctl.get());

                       
                    //又是线程池状态校验
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        //增加这个worker
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    //增加成功后,开启线程
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                //如果开启失败,加入失败
                addWorkerFailed(w);
        }
        return workerStarted;
    } 

 看看new Worker(),可以就看到创建了一个worker,一个worker里面有一个线程,这个线程就是我们的线程池构造方法里面的线程工厂创建的,但是的Runnable是这个Worker

    因为Worker类是实现了了Runnable的,这里有点绕

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable{
         Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
}

  

 当线程调用start方法后,cpu会自动调用run()方法。也就是worker里面的run方法

 

public void run() {
            runWorker(this);
        }


final void runWorker(Worker w) {
        //获取当前线程
        Thread wt = Thread.currentThread();
        //获取任务
        Runnable task = w.firstTask;
        //将这个worker里面任务置为空,
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            //当task不为空
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    //执行任务前要执行的方法,可以扩展,默认为空方法
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        //直接调用run方法
                        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 {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            //执行完了,移除worker,其实这里正常情况下只会移除非核心线程
            processWorkerExit(w, completedAbruptly);
        }
    }

  从阻塞队列里面获取任务

private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?
         //死循环
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

            // Are workers subject to culling?
            //可以看到后面wc>corePoolSize,就是大于核心线程数
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            //这里回收worker,
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                //CAS减少workerCount数量
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                Runnable r = timed ?
                    //多少时候就不等了,这里keepAliveTime其实就是我们传进来的参数,相当于如果大于核心线程数,如果keepAliveTime时候后,仍然取不到,会减少workerCount的数量
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    //阻塞,等待队列里面有任务,核心线程就是,所以核心线程不会退出
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

 

总结与思考:

到这里也就大致明白了几个点:

       第一:核心线程为什么不会消亡?

      因为核心线程从阻塞队列里面取任务时,采用的take(),它是阻塞的。

      第二:非核心线程为什么会消亡?

      其实核心线程和非核心线程是一样的,只是在获取任务时,会判断当前wc(workerCount)数量,如果大于核心线程,就会执行CAS减少worker数量,通过CAS来确保多线程下workerCount数量的正确性,然后移除这个worker。

       第三:一个线程为什么能够执行多个任务?

       其实线程还是只能执行一个任务,只不过这个任务是不断的从阻塞队列里面取出任务,调用它们的run()方法进行执行。

      第四:如果线程次第一个线程执行完了,summit第二个任务时,会新开启一个线程吗?

       会,从源码角度上看,只要线程池数量小于核心线程数,都是先加入线程,当大于核心线程数后,才会进入阻塞队列,从阻塞队列里面取。

posted @ 2022-06-07 16:25  一花一世界!  阅读(65)  评论(0编辑  收藏  举报