线程池篇(一)有的线程

​前提:最近看了别人写的一个关于线程池的,发现好像不太了解,本着代码不会骗人的思想,亲手实验一把。

有的线程它活着,但它躺在池中碌碌无为;有的线程它死了,于是它变成一道面试题。看到这句话不知道为啥有点心酸又有点想笑。

一个线程池中的线程异常了,那么线程池会怎么处理这个线程?

​ 看到这个的时候我的一个反应是:抛出异常,根据线程池定义的策略进行后续的处理,想想看自己“会的真多”。那么代码敲起来,没代码,假把式

public class ExecutorsTestV1 {
    public static void main(String[] args) {
        ThreadPoolExecutor poolExecutor = buildThreadPoolExecutor();
        poolExecutor.execute(()->getThingsDone("execute"));
        poolExecutor.submit(()->getThingsDone("submit"));
       }

    private static ThreadPoolExecutor buildThreadPoolExecutor(){
        int coreSize = 2;
        int maxSize = 10;
        long keepAliveTime = 30*1000;
        LinkedBlockingQueue linkedBlockingQueue = new LinkedBlockingQueue(10);
        ThreadFactory defaultThreadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler defaultHandler = new ThreadPoolExecutor.AbortPolicy();
        /**
         * TODO  手动构造一个线程池的参数
         * int corePoolSize,
         * int maximumPoolSize,
         * long keepAliveTime,
         * TimeUnit unit,
         * BlockingQueue<Runnable> workQueue,
         * ThreadFactory threadFactory,
         * RejectedExecutionHandler handler
         */
        ThreadPoolExecutor threadPoolExecutor =
                new ThreadPoolExecutor(coreSize,maxSize,keepAliveTime,TimeUnit.SECONDS
                ,linkedBlockingQueue,defaultThreadFactory,defaultHandler);
        return threadPoolExecutor;
    }


    private static void getThingsDone(String name){
        String errorMsg = "【 thread-name:"+Thread.currentThread().getName()+" ,搞事情 :"+name+"】";
        System.out.println(errorMsg);
        throw  new RuntimeException(errorMsg+" , 搞事情抛异常!....");
    }

}

executor运行时的异常再发生后 再java.util.concurrent.ThreadPoolExecutor#runWorker中被catch了 并等到运行后执行afterExecute

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LQ3nMTYY-1592671957704)(D:\githubcom\DeceasedLike\src\main\md\线程池.assets\1591890004066.png)]

那么afterExecute执行后这么异常实在什么地方被处理的呢? 这里要说一下关于Thread 和 ThreadGroup ,在Thread init() 方法中可以看到,Thread 会判断是否有 ThreadGroup (参数) ,没有的话从系统中或parent中获取,看起来好像没什么用,敲代码的时候好像没用过? 不急慢慢来。

private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc)
        ....
		Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }
        ....
        this.group = g;
        ....
                      

再看看ThreadGroup对象,翻翻源码看看它在搞什么,里面有一个不看不知道的方法:uncaughtException(Thread t, Throwable e) 源码的文档这么写的:

/**
     * Called by the Java Virtual Machine when a thread in this
     * thread group stops because of an uncaught exception, and the thread
     * does not have a specific {@link Thread.UncaughtExceptionHandler}
     * installed.
     
手动翻译下:当一个thread 在这个 threadgroup 下因为一个 an uncaught exception 而停止,并且该线程没有对应的Thread.UncaughtExceptionHandler(异常处理策略)的时候,这个方法会被JVM 调用

看完后,反过来看看我们自定义的线程池
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QIlSlEyA-1592671957706)(有的线程.assets/1592027796037.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2tSynWYS-1592671957707)(有的线程.assets/1592027813965.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z5fqPNCM-1592671957709)(有的线程.assets/1592027860897.png)]

好像没有关于UncaughtExceptionHandler的定义,来先打个断点,让程序跑一会,毕竟源码不会骗人的,果然进来了


看完了execute() 方法的执行过程,那么submit没有直接抛出异常?源码下一切揭晓:

    /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null); // 变成FutureTask
        execute(ftask); // 调用的还是execute 方法 不过把返回的结果放在了Future 对象中
        return ftask;
    }

在这里插入图片描述

然后当前的 state 是 0 (代表的是 NEW 状态),因为发生异常所以会被改为3(EXCEPTIONAL),然后Future.get() 方法获取FutureTask的执行结果。

看完了代码一切觉得完美,多给写结果线程试试效果,事事不如意十之八九,看看感觉和自己的推理不太一样啊,五个线程为什么出现1,2,3,5,6,如果没有断点的话显示的是1,2,3,4,5,多试几次还出现不同的情况,这是什么意思?
在这里插入图片描述
之前的源码中找到添加task 的地方,然后看看是如何添加的以及线程id 的初始化方式,以及runWorker方法+processWorkerExit方法

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

    private void processWorkerExit(Worker w, boolean completedAbruptly) {
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            completedTaskCount += w.completedTasks;
            // 移除当前发生异常的Worker !!! 所以发生异常的线程会被移除!!!
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        int c = ctl.get();
        if (runStateLessThan(c, STOP)) {
            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);
        }
    }

第一眼看到者两个方法的时候其实有点奇怪,为什么看起来这个processWorkerExit方法不管是正常还是异常都会执行?,不应该啊,异常的时候可以理解正常的任务没必要执行的,结合前一个问题,发现正常的时候并没有执行processWorkerExit方法,那么为什么?从什么地方跳出去了?,debug跑起来多来几个断点,惊喜到处有。

runWorker中有一行代码 while (task != null || (task = getTask()) != null) 看起来很平常,进去看看不得了。

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) :
                workQueue.take(); // workQueue 是一个阻塞队列,当没有任务时会阻塞
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

/**********java.util.concurrent.LinkedBlockingQueue#take**************/
    public E take() throws InterruptedException {
        E x;
        int c = -1;
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();
        try {
            while (count.get() == 0) {
                // notEmpty  ==》 Condition notEmpty
                notEmpty.await();
            }
          ........
    }
/***java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject#await()********/     
 public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
                // 这里这里 
                // LockSupport.park :除非有许可,否则出于线程调度目的禁用当前线程。
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
          ......
        }

所以正常的worker并没有执行processWorkerExit方法,而报异常的会执行processWorkerExit。
看完了代码之后感觉脸被打了,有点疼。平时还是要多敲敲代码,看看源码。

文章搬的这个大佬的:https://mp.weixin.qq.com/s?__biz=MzIxNTQ4MzE1NA==&mid=2247483741&idx=1&sn=238fc933c3b9b19ab1754b23283ac6fd&chksm=9796d720a0e15e364f4105c29de606e7329760a41607136a722dc97bb177f9362aeacd92f762&scene=126&sessionid=1591885069&key=2a95b34f7843ebbf206c92fcb812bfa083e39d317d50216afc66c5ac6d6a1d860b6c8b0285ce8e1234b9bd8242b0582ab430f9f58e7f33fb856ae0023d8ee87e89ac66405d0febcfa66827b13f522079&ascene=1&uin=MTg5MjI0NzkwMg%3D%3D&devicetype=Windows+10+x64&version=62090070&lang=zh_CN&exportkey=AQjS9ua1GI5JCsbSbUs6fj4%3D&pass_ticket=Y5XwQHmAveCFA1CZXgq%2Bo2xHOW19vm5pu8rjrS4nkvNG2P05cWPzC%2BWbuMsqTdQJ

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值