美文网首页面试精选啦啦啦啦啦!Java-多线程
java - ThreadPoolExecutor如何实现线程复

java - ThreadPoolExecutor如何实现线程复

作者: 夹胡碰 | 来源:发表于2021-04-08 14:39 被阅读0次

    1. 线程复用

    我们知道Thread.start执行之后,线程就不能再次执行了,那ThreadPoolExecutor是如何做到线程复用的呢?
    原理很简单,在实际执行的线程外部套一个Thread,外层Threadrun方法while循环执行实际执行线程的run方法,实现线程的复用并且执行之后不销毁。下面是伪代码:

    // 任务等待队列
    BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue();
    
    new Thread(() -> {
        for (;;){
            Runnable runnable = taskQueue.poll();//队列里拿
            runnable.run();//同步执行
        }
    }).start();// 异步while执行
    

    下面是ThreadPoolExecutor的重点代码:

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            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(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 {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }
    

    2. 线程销毁

    我们知道,在创建线程池的时候有超时参数keepAliveTime,那么线程池是如何实现精确的超时销毁呢?
    这个是结合BlockingQueue的阻塞超时来实现的,下面是源码:

     /**
     * ...
     * @return task, or null if the worker must exit, in which case workerCount is decremented
     * 翻译: 返回task,如果worker必须退出,则返回null,在这种情况下workerCount递减
     */
    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?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
    
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }
    
            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : // 重点在这,如果超时没有获取到任务,则返回null,销毁线程。
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }
    

    3. 为什么用BlockingQueue

    1. 获取等待任务的时候,直接用阻塞代替通知轮询,提高性能,减少代码复杂度。
    2. 复用阻塞超时获取等待任务实现线程超时销毁,设计精巧。
    3. 本身就是支持并发操作的,不用额外维护线程安全。

    参考

    1. 一. 线程池简介

    相关文章

      网友评论

        本文标题:java - ThreadPoolExecutor如何实现线程复

        本文链接:https://www.haomeiwen.com/subject/arfvkltx.html