• 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第二个任务时,会新开启一个线程吗?

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

  • 相关阅读:
    ASM ClassReader failed to parse class file
    idea运行java项目js中文乱码如何解决
    Error:(182, 32) java: 常量字符串过长
    ssh启动报错:org.dom4j.DocumentException: Connection timed out: connect Nested exception: Connection timed out: connect
    [Intro to Deep Learning with PyTorch -- L2 -- N14] Sigmoid function
    [CSS3] CSS Selector
    [HTML5] document.activeElement
    [Intro to Deep Learning with PyTorch -- L2 -- N9] Perceptron Trick
    [Javascript] Broadcaster, operator, listener pattern: Write a debounce operator -- 1
    [CSS] place-content = align-items + justify-content
  • 原文地址:https://www.cnblogs.com/minblog/p/16352356.html
Copyright © 2020-2023  润新知