Java线程池ThreadPoolExecutor原理解析

Java线程池ThreadPoolExecutor工作原理解析 总结来说,ThreadPoolExecutor是Java中用于管理线程池的类,它通过核心线程池、任务队列、最大线程数、线程工厂和拒绝策略等机制来实现线程的调度和复用,从而提高程序的性能和资源利用率。在上面的示例中,我们创建了一个ThreadPoolExecutor实例,并设置了核心线程池大小为5,最大线程池大小为10,线程空闲时间为5000毫秒,任务队列大小为100。当任务执行完毕后,线程会返回线程池,并可以被复用来执行其他任务,从而避免了频繁创建和销毁线程的开销,提高了性能和资源利用率。 阅读详情

    在Java中,使用线程池来异步执行一些耗时任务是非常常见的操作。最初我们一般都是直接使用new Thread().start的方式,但我们知道,线程的创建和销毁都会耗费大量的资源, 因此我们需要重用线程资源。

继承结构:

继承结构看起来很清晰,最顶层的Executor只提供了一个最简单的void execute(Runnable command)方法,然后是ExecutorService,ExecutorService提供了一些管理相关的方法,例如关闭、判断当前线程池的状态等,另外不同于Executor#execute,ExecutorService提供了一系列方法,可以将任务包装成一个Future,从而使得任务提交方可以跟踪任务的状态。而父类AbstractExecutorService则提供了一些默认的实现。

构造器

ThreadPoolExecutor的构造器提供了非常多的参数,每一个参数都非常的重要,一不小心就容易踩坑,因此设置的时候,你必须要知道自己在干什么。

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;
    }

1. corePoolSize、 maximumPoolSize。线程池会自动根据corePoolSize和maximumPoolSize去调整当前线程池的大小。当你通过submit或者execute方法提交任务的时候,如果当前线程池的线程数小于corePoolSize,那么线程池就会创建一个新的线程处理任务, 即使其他的core线程是空闲的。如果当前线程数大于corePoolSize并且小于maximumPoolSize,那么只有在队列"满"的时候才会创建新的线程。因此这里会有很多的坑,比如你的core和max线程数设置的不一样,希望请求积压在队列的时候能够实时的扩容,但如果制定了一个无界队列,那么就不会扩容了,因为队列不存在满的概念。

2. keepAliveTime。如果当前线程池中的线程数超过了corePoolSize,那么如果在keepAliveTime时间内都没有新的任务需要处理,那么超过corePoolSize的这部分线程就会被销毁。默认情况下是不会回收core线程的,可以通过设置allowCoreThreadTimeOut改变这一行为。

3. workQueue。即实际用于存储任务的队列,这个可以说是最核心的一个参数了,直接决定了线程池的行为,比如说传入一个有界队列,那么队列满的时候,线程池就会根据core和max参数的设置情况决定是否需要扩容,如果传入了一个SynchronousQueue,这个队列只有在另一个线程在同步remove的时候才可以put成功,对应到线程池中,简单来说就是如果有线程池任务处理完了,调用poll或者take方法获取新的任务的时候,新提交的任务才会put成功,否则如果当前的线程都在忙着处理任务,那么就会put失败,也就会走扩容的逻辑,如果传入了一个DelayedWorkQueue,顾名思义,任务就会根据过期时间来决定什么时候弹出,即为ScheduledThreadPoolExecutor的机制。

4. threadFactory。创建线程都是通过ThreadFactory来实现的,如果没指定的话,默认会使用Executors.defaultThreadFactory() ,一般来说,我们会在这里对线程设置名称、异常处理器等。

5. handler。即当任务提交失败的时候,会调用这个处理器,ThreadPoolExecutor内置了多个实现,比如抛异常、直接抛弃等。这里也需要根据业务场景进行设置,比如说当队列积压的时候,针对性的对线程池扩容或者发送告警等策略。

看完这几个参数的含义,我们看一下Executors提供的一些工具方法,只要是为了方便使用,但是我建议最好少用这个类,而是直接用ThreadPoolExecutor的构造函数,多了解一下这几个参数到底是什么意思,自己的业务场景是什么样的,比如线程池需不需要扩容、用不用回收空闲的线程等。

public class Executors {
 
    /*
    * 提供一个固定大小的线程池,并且线程不会回收,由于传入的是一个无界队列,相当于队列永远不会满
    * 也就不会扩容,因此需要特别注意任务积压在队列中导致内存爆掉的问题
    */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
 
 
    /*
    *  这个线程池会一直扩容,由于SynchronousQueue的特性,如果当前所有的线程都在处理任务,那么
    *  新的请求过来,就会导致创建一个新的线程处理任务。如果线程一分钟没有新任务处理,就会被回 
    *  收掉。特别注意,如果每一个任务都比较耗时,并发又比较高,那么可能每次任务过来都会创建一个线 
    *  程
    */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
}

源码分析

既然是个线程池,那就必然有其生命周期:运行中、关闭、停止等。ThreadPoolExecutor是用一个AtomicInteger去的前三位表示这个状态的,另外又重用了低29位用于表示线程数,可以支持最大大概5亿多,绝逼够用了,如果以后硬件真的发展到能够启动这么多线程,改成AtomicLong就可以了。
状态这里主要分为下面几种:

    1. RUNNING: 表示当前线程池正在运行中,可以接受新任务以及处理队列中的任务

    2. SHUTDOWN: 不再接受新的任务,但会继续处理队列中的任务

    3. STOP: 不再接受新的任务,也不处理队列中的任务了,并且会中断正在进行中的任务

    4. TIDYING: 所有任务都已经处理完毕,线程数为0,转为为TIDYING状态之后,会调用terminated()回调

    5. TERMINATED: terminated()已经执行完毕

同时我们可以看到所有的状态都是用二进制位表示的,并且依次递增,从而方便进行比较,比如想获取当前状态是否至少为SHUTDOWN等,同时状态之前有几种转换:

    1. RUNNING -> SHUTDOWN。调用了shutdown()之后,或者执行了finalize()

    2. (RUNNING 或者 SHUTDOWN) -> STOP。调用了shutdownNow()之后会转换这个状态

    3. SHUTDOWN -> TIDYING。当线程池和队列都为空的时候

    4. STOP -> TIDYING。当线程池为空的时候

    5. IDYING -> TERMINATED。执行完terminated()回调之后会转换为这个状态

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
 
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;
 
    //由于前三位表示状态,因此将CAPACITY取反,和进行与操作即可
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
 
    //高三位+第三位进行或操作即可
    private static int ctlOf(int rs, int wc) { return rs | wc; }
 
    private static boolean runStateLessThan(int c, int s) {
        return c < s;
    }
 
    private static boolean runStateAtLeast(int c, int s) {
        return c >= s;
    }
 
    private static boolean isRunning(int c) {
        return c < SHUTDOWN;
    }
 
    //下面三个方法,通过CAS修改worker的数目
    private boolean compareAndIncrementWorkerCount(int expect) {
        return ctl.compareAndSet(expect, expect + 1);
    }
 
    //只尝试一次,失败了则返回,是否重试由调用方决定
    private boolean compareAndDecrementWorkerCount(int expect) {
        return ctl.compareAndSet(expect, expect - 1);
    }
 
    //跟上一个不一样,会一直重试
    private void decrementWorkerCount() {
        do {} while (! compareAndDecrementWorkerCount(ctl.get()));
    }

下面是比较核心的字段,这里workers采用的是非线程安全的HashSet, 而不是线程安全的版本,主要是因为这里有些复合的操作,比如说将worker添加到workers后,我们还需要判断是否需要更新largestPoolSize等,workers只在获取到mainLock的情况下才会进行读写,另外这里的mainLock也用于在中断线程的时候串行执行,否则如果不加锁的话,可能会造成并发去中断线程,引起不必要的中断风暴。

private final ReentrantLock mainLock = new ReentrantLock();
 
private final HashSet<Worker> workers = new HashSet<Worker>();
 
private final Condition termination = mainLock.newCondition();
 
private int largestPoolSize;
 
private long completedTaskCount;

核心方法

拿到一个线程池之后,我们就可以开始提交任务,让它去执行了,那么我们看一下submit方法是如何实现的。

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

这两个方法都很简单,首先将提交过来的任务(有两种形式:Callable、Runnable )都包装成统一的 RunnableFuture,然后调用execute方法,execute可以说是线程池最核心的一个方法。

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        /*
            获取当前worker的数目,如果小于corePoolSize那么就扩容,
            这里不会判断是否已经有core线程,而是只要小于corePoolSize就会直接增加worker
         */
        if (workerCountOf(c) < corePoolSize) {
            /*
                调用addWorker(Runnable firstTask, boolean core)方法扩容
                firstTask表示为该worker启动之后要执行的第一个任务,core表示要增加的为core线程
             */
            if (addWorker(command, true))
                return;
            //如果增加失败了那么重新获取ctl的快照,比如可能线程池在这期间关闭了
            c = ctl.get();
        }
        /*
             如果当前线程池正在运行中,并且将任务丢到队列中成功了,
             那么就会进行一次double check,看下在这期间线程池是否关闭了,
             如果关闭了,比如处于SHUTDOWN状态,如上文所讲的,SHUTDOWN状态的时候,
             不再接受新任务,remove成功后调用拒绝处理器。而如果仍然处于运行中的状态,
             那么这里就double check下当前的worker数,如果为0,有可能在上述逻辑的执行
             过程中,有worker销毁了,比如说任务抛出了未捕获异常等,那么就会进行一次扩容,
             但不同于扩容core线程,这里由于任务已经丢到队列中去了,因此就不需要再传递firstTask了,
             同时要注意,这里扩容的是非core线程
         */
        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))
            /*
                如果在上一步中,将任务丢到队列中失败了,那么就进行一次扩容,
                这里会将任务传递到firstTask参数中,并且扩容的是非core线程,
                如果扩容失败了,那么就执行拒绝策略。
             */
            reject(command);
    }

这里要特别注意下防止队列失败的逻辑,不同的队列丢任务的逻辑也不一样,例如说无界队列,那么就永远不会put失败,也就是说扩容也永远不会执行,如果是有界队列,那么当队列满的时候,会扩容非core线程,如果是SynchronousQueue,这个队列比较特殊,当有另外一个线程正在同步获取任务的时候,你才能put成功,因此如果当前线程池中所有的worker都忙着处理任务的时候,那么后续的每次新任务都会导致扩容, 当然如果worker没有任务处理了,阻塞在获取任务这一步的时候,新任务的提交就会直接丢到队列中去,而不会扩容。
上文中多次提到了扩容,那么我们下面看一下线程池具体是如何进行扩容的:

private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            //获取当前线程池的状态
            int rs = runStateOf(c);
 
            /*
                如果状态为大于SHUTDOWN, 比如说STOP,STOP上文说过队列中的任务不处理了,也不接受新任务,
                因此可以直接返回false不扩容了,如果状态为SHUTDOWN并且firstTask为null,同时队列非空,
                那么就可以扩容
             */
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                    firstTask == null &&
                    ! workQueue.isEmpty()))
                return false;
 
            for (;;) {
                int wc = workerCountOf(c);
                /*
                    若worker的数目大于CAPACITY则直接返回,
                    然后根据要扩容的是core线程还是非core线程,进行判断worker数目
                    是否超过设置的值,超过则返回
                 */
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                /*
                    通过CAS的方式自增worker的数目,成功了则直接跳出循环
                 */
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                //重新读取状态变量,如果状态改变了,比如线程池关闭了,那么就跳到最外层的for循环,
                //注意这里跳出的是retry。
                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 {
                    /*
                        获取锁,并判断线程池是否已经关闭
                     */
                    int rs = runStateOf(ctl.get());
 
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // 若线程已经启动了,比如说已经调用了start()方法,那么就抛异常,
                            throw new IllegalThreadStateException();
                        //添加到workers中
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize) //更新largestPoolSize
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    //若Worker创建成功,则启动线程,这么时候worker就会开始执行任务了
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                //添加失败
                addWorkerFailed(w);
        }
        return workerStarted;
    } 
 
    private void addWorkerFailed(Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (w != null)
                workers.remove(w);
            decrementWorkerCount();
            //每次减少worker或者从队列中移除任务的时候都需要调用这个方法
            tryTerminate();
        } finally {
            mainLock.unlock();
        }
    }

这里有个貌似不太起眼的方法tryTerminate,这个方法会在所有可能导致线程池终结的地方调用,比如说减少worker的数目等,如果满足条件的话,那么将线程池转换为TERMINATED状态。另外这个方法没有用private修饰,因为ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,而ScheduledThreadPoolExecutor也会调用这个方法。

final void tryTerminate() {
        for (;;) {
            int c = ctl.get();
            /*
                如果当前线程处于运行中、TIDYING、TERMINATED状态则直接返回,运行中的没
                什么好说的,后面两种状态可以说线程池已经正在终结了,另外如果处于SHUTDOWN状态,
                并且workQueue非空,表明还有任务需要处理,也直接返回
             */
            if (isRunning(c) ||
                runStateAtLeast(c, TIDYING) ||
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
                return;
            //可以退出,但是线程数非0,那么就中断一个线程,从而使得关闭的信号能够传递下去,
            //中断worker后,worker捕获异常后,会尝试退出,并在这里继续执行tryTerminate()方法,
            //从而使得信号传递下去
            if (workerCountOf(c) != 0) {
                interruptIdleWorkers(ONLY_ONE);
                return;
            }
 
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                //尝试转换成TIDYING状态,执行完terminated回调之后
                //会转换为TERMINATED状态,这个时候线程池已经完整关闭了,
                //通过signalAll方法,唤醒所有阻塞在awaitTermination上的线程
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                    try {
                        terminated();
                    } finally {
                        ctl.set(ctlOf(TERMINATED, 0));
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
                mainLock.unlock();
            }
            // else retry on failed CAS
        }
    }
 
    /**
     * 中断空闲的线程
     * @param onlyOne
     */
    private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers) {
                //遍历所有worker,若之前没有被中断过,
                //并且获取锁成功,那么就尝试中断。
                //锁能够获取成功,那么表明当前worker没有在执行任务,而是在
                //获取任务,因此也就达到了只中断空闲线程的目的。
                Thread t = w.thread;
                if (!t.isInterrupted() && w.tryLock()) {
                    try {
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        w.unlock();
                    }
                }
                if (onlyOne)
                    break;
            }
        } finally {
            mainLock.unlock();
        }
    }

Worker

下面看一下Worker类,也就是这个类实际负责执行任务,Worker类继承自AbstractQueuedSynchronizer,AQS可以理解为一个同步框架,提供了一些通用的机制,利用模板方法模式,让你能够原子的管理同步状态、blocking和unblocking线程、以及队列,具体的内容之后有时间会再写,还是比较复杂的。这里Worker对AQS的使用相对比较简单,使用了状态变量state表示是否获得锁,0表示解锁、1表示已获得锁,同时通过exclusiveOwnerThread存储当前持有锁的线程。另外再简单提一下,比如说CountDownLatch, 也是基于AQS框架实现的,countdown方法递减state,await阻塞等待state为0。

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
 
        /** Thread this worker is running in.  Null if factory fails. */
        final Thread thread;
 
        /** Initial task to run.  Possibly null. */
        Runnable firstTask;
 
        /** Per-thread task counter */
        volatile long completedTasks;
 
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
 
        /** Delegates main run loop to outer runWorker  */
        public void run() {
            runWorker(this);
        }
       protected boolean isHeldExclusively() {
            return getState() != 0;
        }
 
        protected boolean tryAcquire(int unused) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
 
        protected boolean tryRelease(int unused) {
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
 
        public void lock()        { acquire(1); }
        public boolean tryLock()  { return tryAcquire(1); }
        public void unlock()      { release(1); }
        public boolean isLocked() { return isHeldExclusively(); }
 
        void interruptIfStarted() {
            Thread t;
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }
    }

注意这里Worker初始化的时候,会通过setState(-1)将state设置为-1,并在runWorker()方法中置为0,上文说过Worker是利用state这个变量来表示锁的状态,那么加锁的操作就是通过CAS将state从0改成1,那么初始化的时候改成-1,也就是表示在Worker启动之前,都不允许加锁操作,我们再看interruptIfStarted()以及interruptIdleWorkers()方法,这两个方法在尝试中断Worker之前,都会先加锁或者判断state是否大于0,因此这里的将state设置为-1,就是为了禁止中断操作,并在runWorker中置为0,也就是说只能在Worker启动之后才能够中断Worker。
另外线程启动之后,其实就是调用了runWorker方法,下面我们看一下具体是如何实现的。

 final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // 调用unlock()方法,将state置为0,表示其他操作可以获得锁或者中断worker
        boolean completedAbruptly = true;
        try {
            /*
                首先尝试执行firstTask,若没有的话,则调用getTask()从队列中获取任务
             */
            while (task != null || (task = getTask()) != null) {
                w.lock();
                /*
                    如果线程池正在关闭,那么中断线程。
                 */
                if ((runStateAtLeast(ctl.get(), STOP) ||
                    (Thread.interrupted() &&
                        runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    //执行beforeExecute回调
                    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回调
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    //这里加了锁,因此没有线程安全的问题,volatile修饰保证其他线程的可见性
                    w.completedTasks++;
                    w.unlock();//解锁
                }
            }
            completedAbruptly = false;
        } finally {
            //抛异常了,或者当前队列中已没有任务需要处理等
            processWorkerExit(w, completedAbruptly);
        }
    }
 
private void processWorkerExit(Worker w, boolean completedAbruptly) {
        //如果是异常终止的,那么减少worker的数目
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();
 
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            //将当前worker中workers中删除掉,并累加当前worker已执行的任务到completedTaskCount中
            completedTaskCount += w.completedTasks;
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }
 
        //上文说过,减少worker的操作都需要调用这个方法
        tryTerminate();
 
        /*
            如果当前线程池仍然是运行中的状态,那么就看一下是否需要新增另外一个worker替换此worker
         */
        int c = ctl.get();
        if (runStateLessThan(c, STOP)) {
            /*
                如果是异常结束的则直接扩容,否则的话则为正常退出,比如当前队列中已经没有任务需要处理,
                如果允许core线程超时的话,那么看一下当前队列是否为空,空的话则不用扩容。否则话看一下
                是否少于corePoolSize个worker在运行。
             */
            if (!completedAbruptly) {
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            addWorker(null, false);
        }
    }
 
     private Runnable getTask() {
        boolean timedOut = false; // 上一次poll()是否超时了
 
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);
 
            // 若线程池关闭了(状态大于STOP)
            // 或者线程池处于SHUTDOWN状态,但是队列为空,那么返回null
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }
 
            int wc = workerCountOf(c);
 
            /*
                如果允许core线程超时 或者 不允许core线程超时但当前worker的数目大于core线程数,
                那么下面的poll()则超时调用
             */
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
 
            /*
                获取任务超时了并且(当前线程池中还有不止一个worker 或者 队列中已经没有任务了),那么就尝试
                减少worker的数目,若失败了则重试
             */
            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;
                //走到这里表明,poll调用超时了
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

关闭线程池

关闭线程池一般有两种形式,shutdown()和shutdownNow()

public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            //通过CAS将状态更改为SHUTDOWN,这个时候线程池不接受新任务,但会继续处理队列中的任务
            advanceRunState(SHUTDOWN);
            //中断所有空闲的worker,也就是说除了正在处理任务的worker,其他阻塞在getTask()上的worker
            //都会被中断
            interruptIdleWorkers();
            //执行回调
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        //这个方法不会等待所有的任务处理完成才返回
    }
    public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            /*
                不同于shutdown(),会转换为STOP状态,不再处理新任务,队列中的任务也不处理,
                而且会中断所有的worker,而不只是空闲的worker
             */
            advanceRunState(STOP);
            interruptWorkers();
            tasks = drainQueue();//将所有的任务从队列中弹出
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
    }
 
    private List<Runnable> drainQueue() {
        BlockingQueue<Runnable> q = workQueue;
        ArrayList<Runnable> taskList = new ArrayList<Runnable>();
        /*
            将队列中所有的任务remove掉,并添加到taskList中,
            但是有些队列比较特殊,比如说DelayQueue,如果第一个任务还没到过期时间,则不会弹出,
            因此这里通过调用toArray方法,然后再一个一个的remove掉
         */
        q.drainTo(taskList);
        if (!q.isEmpty()) {
            for (Runnable r : q.toArray(new Runnable[0])) {
                if (q.remove(r))
                    taskList.add(r);
            }
        }
        return taskList;
    }

从上文中可以看到,调用了shutdown()方法后,不会等待所有的任务处理完毕才返回,因此需要调用awaitTermination()来实现

public boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException {
        long nanos = unit.toNanos(timeout);
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (;;) {
                //线程池若已经终结了,那么就返回
                if (runStateAtLeast(ctl.get(), TERMINATED))
                    return true;
                //若超时了,也返回掉
                if (nanos <= 0)
                    return false;
                //阻塞在信号量上,等待线程池终结,但是要注意这个方法可能会因为一些未知原因随时唤醒当前线程,
                //因此需要重试,在tryTerminate()方法中,执行完terminated()回调后,表明线程池已经终结了,
                //然后会通过termination.signalAll()唤醒当前线程
                nanos = termination.awaitNanos(nanos);
            }
        } finally {
            mainLock.unlock();
        }
    }

一些统计相关的方法

 public int getPoolSize() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            //若线程已终结则直接返回0,否则计算works中的数目
           //想一下为什么不用workerCount呢?
            return runStateAtLeast(ctl.get(), TIDYING) ? 0
                : workers.size();
        } finally {
            mainLock.unlock();
        }
    }
 
   public int getActiveCount() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            int n = 0;
            for (Worker w : workers)
                if (w.isLocked())//上锁的表明worker当前正在处理任务,也就是活跃的worker
                    ++n;
            return n;
        } finally {
            mainLock.unlock();
        }
    }
 
 
    public int getLargestPoolSize() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            return largestPoolSize;
        } finally {
            mainLock.unlock();
        }
    }
 
    //获取任务的总数,这个方法慎用,若是个无解队列,或者队列挤压比较严重,会很蛋疼
    public long getTaskCount() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            long n = completedTaskCount;//比如有些worker被销毁后,其处理完成的任务就会叠加到这里
            for (Worker w : workers) {
                n += w.completedTasks;//叠加历史处理完成的任务
                if (w.isLocked())//上锁表明正在处理任务,也算一个
                    ++n;
            }
            return n + workQueue.size();//获取队列中的数目
        } finally {
            mainLock.unlock();
        }
    }
 
 
    public long getCompletedTaskCount() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            long n = completedTaskCount;
            for (Worker w : workers)
                n += w.completedTasks;
            return n;
        } finally {
            mainLock.unlock();
        }
    }

总结

这篇博客基本上覆盖了线程池的方方面面,但仍然有非常多的细节可以深究,比如说异常的处理,另外还有AQS、unsafe等。

C#串口通讯实战:如何用SerialPort类搞定工业传感器数据采集(附完整代码) 本文详细介绍了如何使用C#的SerialPort类实现工业级传感器数据采集,涵盖串口通讯基础配置、数据帧处理、异常恢复机制及性能优化策略。通过实战代码示例,帮助开发者构建稳定可靠的工业数据采集系统,特别适合需要处理高频数据采集和复杂工业环境的场景。 阅读详情

相关推荐

技术指南| 叶绿体基因组高级分析全流程解析

本文详细解析了叶绿体基因组高级分析的全流程,包括实验设计、序列组装与注释、比较基因组分析、选择压力与功能进化等关键步骤。通过实战案例和技术技巧,帮助研究者掌握叶绿体基因组分析的核心方法,提升植物进化研究和系统学分析的效率与准确性。

weixin_33725239的博客 491

Java线程池源码解析

Java线程池基础传送门:Java线程池基础及AtomicInteger Java线程池原理 除了newScheduledThreadPool,其他线程池都是基于ThreadPoolExecutor实现的。 线程池内部状态 既然是线程池,必然存在生命周期:运行中,关闭,停止等。ThreadPoolExecutor是用AtomicInteger变量ctl前三位表示这个状态的,另外用了后29位表示线程...

diaog的博客 531

yolov8的Detect层详解(输出维度改动)

对于部署端需要拿到onnx的output进行后处理,但是基于anchor_free的yolov8输出是1*(4+cls)*8400,不是我们常见的ncwh格式,所以对Detect层进行了学习,分享一下体会。

yjcccccc的博客 2万+

线程池为什么能维持线程不释放,随时运行各种任务?

线程池 之前一直有这个疑问:我们平时使用线程都是各种new Thread(),然后直接在run()方法里面执行我们要做的各种操作,使用完后需要做什么管理吗?线程池为什么能维持住核心线程不释放,一直接收任务进行处理呢? 线程 线程无他,主要有两个方法,我们先看看start()方法介绍: /** * Causes this thread to begin execution;

维斯特洛(Westeros)大陆 1万+

5分钟搞定!用L型匹配电路优化窄带天线性能(附Smith圆图实操)

本文针对窄带天线阻抗匹配难题,提供了一套5分钟快速解决方案。通过详解L型匹配电路的核心原理,并结合Smith圆图这一射频工程师的“瑞士军刀”,手把手指导如何选择拓扑、计算元件值并进行实际调试,旨在帮助硬件工程师高效优化天线性能,实现最大功率传输。

yyy34的博客 443

Simulink玩转PMSM无感FOC:从IF强拖参数调试到切换失败的避坑指南

本文深入探讨了Simulink在PMSM无感FOC控制中的IF强拖启动参数调试与切换避坑技巧。通过详细解析强拖加速、速度维持和闭环切换三大阶段的设计逻辑,以及五个常见陷阱的解决方案,帮助工程师快速定位启动失败和切换震荡问题。文章还提供了典型故障的波形诊断方法和高级调试工具链优化建议,是电机控制领域的实用指南。

weixin_29164843的博客 408

java中executors_Java通过Executors提供四种线程池

http://cuisuqiang.iteye.com/blog/2019372Java通过Executors提供四种线程池,分别为:newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。newScheduledTh...

weixin_42548893的博客 789

在Android Jetpack Compose中实现夜间模式

Android基于Compose的深色主题实现

虎哥LoveDroid 1235

线程池(ThreadPoolExecutor)源码解析

目录一、线程池概念介绍1.1、什么是线程池1.2、线程池工作流程1.3、线程池的代码用例二、源码解析——构造函数2.1、线程池的构造函数及类的继承关系2.2、Executors提供的线程池模板2.3、拒绝策略的实现2.4、任务队列BlockingQueue的实现三、 源码解析——execute(Runnable command)3.1、流程概述 一、线程池概念介绍 1.1、什么是线程池 池化技术其实在技术生态圈中是比较常见的,比如:对象池、连接池等等。 为什么会有线程池这个东西存在呢?其实原因跟其他池化技

小志的博客 8748

HDOCK 蛋白蛋白对接软件使用;LZerD、pyDock、ClusPro网页在线对接

参考:hdock支持蛋白与蛋白,也支持蛋白与核酸对接计算。

weixin_42357472的博客 1万+

Codex 调试记录获取完全指南:日志查看、工具调用与实战排查教程

本文主要介绍了Codex 调试记录获取完全指南:日志查看、工具调用与实战排查教程,希望能对使用Codex的同学们有所帮助。 文章目录 1. 背景 2. 实战教程

weixin_43178406的博客 6万+

关于netty服务端使用SocketChannel报Failed to submit a listener notification task. Event loop shut down?问题

Failed to submit a listener notification task. Event loop shut down? java.util.concurrent.RejectedExecutionException: event executor terminated 这是博主在开发过程中发现的报错。 关于这个问题,关键在于服务端在每次连客户端是都会新一个SocketChannel通道来区分多个客户端,因为博主之前是每生成一个Channel就存到了socketChannelMaps中,代码

qq_41081286的博客 2万+

cesium实战目录必看

本文作者长期从事GIS相关工作,专注于三维和地图可视化领域,熟悉Cesium、Three.js、WebGL等开发框架。文章目录展示了作者在Cesium应用中的多项技术实践,涵盖水面反射、离屏渲染、后处理效果、地形裁剪、Shader着色器、动态单体化、气象图、缓冲区域、坡度和坡向分析、三维热力图、通视分析、态势标绘等多个方面。这些内容为开发者提供了丰富的技术参考,展示了Cesium在三维可视化中的强大功能和应用场景。

God's asylum的博客 8718

java的线程池ThreadPoolExecutor原理解析

多线程问题,很多时候都会用到线程池,而ThreadPoolExecutor是java中线程池的重要实现类,理解ThreadPoolExecutor有助于我们更好地理解线程池、甚至去自定义自己的线程池。 基本使用 初始化线程池 Executor executor=new ThreadPoolExecutor(0,Integer.MAX_VALUE,60, TimeUnit.MILLISECOND...

qq_23081779的博客 681

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

Java线程池:ThreadPoolExecutor原理解析

本文介绍了线程池的基本概念、主要参数、工作流程,以及 execute() 方法的源码分析,此外,还讨论了在实际应用中可能遇到的陷阱和问题。

u014800975的博客 1232

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

JAVA基础:线程池ThreadPoolExecutor及Future原理解析

JAVA基础:线程池ThreadPoolExecutor及Future原理解析线程池的作用ThreadPoolExecutor实现原理 线程池的作用 为了降低在处理短时间任务创建与销毁线程的代价。 控制线程的数量,过多的线程会带来调度的开销以及资源的占用。 一定程度上提高程序的稳定性和可维护性,防止出现意外创建过多线程的情况导致资源占用满了出现GC问题甚至导致程序崩溃。 ThreadPoolE...

weixin_44366439的博客 2832

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

Java线程池架构原理和源码解析(ThreadPoolExecutor)

在前面介绍JUC的文章中,提到了关于线程池Execotors的创建介绍,在文章:《java之JUC系列-外部Tools》中第一部分有详细的说明,请参阅; 文章中其实说明了外部的使用方式,但是没有说内部是如何实现的,为了加深对实现的理解,在使用中可以放心,我们这里将做源码解析以及反馈到原理上,Executors工具可以创建普通的线程池以及schedule调度任务的调度池,其实两者实现上还是有一

IT_驿站 388

Java 线程池架构原理和源码解析(ThreadPoolExecutor)

使用Executors最常用的莫过于是使用:Executors.newFixedThreadPool(int)这个方法,因为它既可以限制数量,而且线程用完后不会一直被ca...

田维常 375

Java线程池原理解析-ThreadPoolExecutor

【代码】Java线程池原理解析-ThreadPoolExecutor。

admin3211的博客 653

Java并发编程中的线程池深入解析ThreadPoolExecutor的工作原理与实战应用

ThreadPoolExecutor提供了四种内置策略:AbortPolicy(默认,抛出RejectedExecutionException)、CallerRunsPolicy(由调用者线程执行任务)、DiscardPolicy(静默丢弃任务)和DiscardOldestPolicy(丢弃队列最老任务并重试)。CPU密集型任务建议设置较小的线程数(通常为CPU核数+1),而I/O密集型任务可设置较大线程数。线程池的任务调度遵循特定的优先级:核心线程优先使用,其次任务排队,最后才考虑创建非核心线程。

xyj4189的博客 630

Java并发编程实战深度解析线程池ThreadPoolExecutor的设计原理与性能优化策略

其设计基于生产者-消费者模式,通过内部维护的工作线程池来执行提交的任务,从而避免频繁创建和销毁线程带来的性能开销。其核心构造参数包括:corePoolSize(核心线程数)、maximumPoolSize(最大线程数)、keepAliveTime(线程空闲时间)、workQueue(工作队列)以及RejectedExecutionHandler(拒绝策略)。线程池根据当前负载动态调整线程数量,当核心线程满载且工作队列已满时,才会创建新线程直至达到最大线程数,这种设计有效平衡了资源消耗与系统吞吐量。

kkjt0130的博客 761

Java线程池ThreadPoolExecutor核心参数与工作原理解析

在Java并发编程中,线程池是一种非常重要的工具,它能够有效地管理线程资源,避免频繁创建和销毁线程带来的性能开销。通过`Executors`工具类可以快速创建一些常见配置的线程池(如`newFixedThreadPool`、`newCachedThreadPool`),但在生产环境中,建议直接通过ThreadPoolExecutor的构造函数来创建,以便更精确地控制所有参数,避免潜在风险。当新任务提交时,如果当前线程数小于corePoolSize,即使存在空闲线程,线程池也会创建一个新线程来处理任务。

currypop17的博客 962

JAVA8线程池THREADPOOLEXECUTOR底层原理及其源码解析

小侃一下 1. 使用线程池的好处. 为什么要使用线程池? 2. 线程池核心参数介绍 3. 提交任务到线程池中的流程 3.1 ThreadPoolExecutor#execute方法整体流程 3.2 排队恰火锅的场景 4. ThreadPoolExecutor线程池源码及其原理 4.1 从创建ThreadPoolExecutor开始: 线程池构造函数的源码 4.2 ThreadPoolExecutor中的一些重要的属性 4.2.1 线程池的运行状态 4.2.2 核心属性ctl

li123128的博客 496

深入理解ThreadPoolExecutor线程池工作原理源码解析_java threadpoolexecutor源码

其他2个参数我就不讲了,作用基本上在源码解析中可以提现。

2401_84692538的博客 719

《Java线程池ThreadPoolExecutor核心参数与工作原理解析》

(如LinkedBlockingQueue):可以无限添加任务,此时maximumPoolSize参数无效,线程池最多创建corePoolSize个线程,适用于任务提交速率稳定的场景。:线程池允许创建的最大线程数量。当工作队列已满且当前线程数小于最大线程数时,线程池会创建新的线程来处理任务。类提供了强大的线程池实现,它能够高效地管理线程生命周期和执行任务,避免频繁创建和销毁线程带来的性能开销。对于IO密集型任务,可以设置更多线程,因为线程大部分时间在等待IO操作,建议线程数设置为CPU核心数2。

Hboxod的博客 553
上一篇: Java面试(进大厂不是梦)
下一篇: Java并发锁
一尾鲫鱼
博客等级 码龄9年 7粉丝 18原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值