season8 最近的时间轴更新
season8

season8

V2EX 第 407853 号会员,加入于 2019-04-30 12:47:50 +08:00
办公室不开灯,倍感舒适,我是夜猫吗?
  •  2   
    程序员  •  season8  •  58 天前  •  最后回复来自 stuartTin
    42
    请教一个 Java 多线程嵌套使用的问题
    问与答  •  season8  •  184 天前  •  最后回复来自 season8
    13
    五年渣猿,还犯低级 bug,如何提高开发质量
    程序员  •  season8  •  2019-05-17 15:21:26 PM  •  最后回复来自 526326991
    29
    season8 最近回复了
    还有更离谱的,我寻思居然每人吐槽,猫宝,狗东,蚂蚁宝 每次打开自动导航活动页,按返回键会退回到桌面,每次使用要打开两次,要不是没找到合适替代,早就卸载了!
    58 天前
    回复了 season8 创建的主题 程序员 办公室不开灯,倍感舒适,我是夜猫吗?
    @FS1P7dJz 好像有这个原因,余光中一排灯管,用手遮住眼睛四周发现舒服不少
    58 天前
    回复了 season8 创建的主题 程序员 办公室不开灯,倍感舒适,我是夜猫吗?
    @IGJacklove 我看了几篇文章,大致的结论是:1. 暗色模式能减少对眼睛刺激,但不是护眼模式,因为 [虹膜在白底环境不需太过扩张来吸收更多光线,相比之下,黑底的环境则需要虹膜扩张,因此长期使用暗黑模式,眼睛的肌群其实会更容易疲劳] <br>
    2.由于虹膜明亮模式下不需要过度扩张,会很容易看清文字,单暗黑模式容易产生光晕效应 <br>
    3.眼睛看明亮模式难以适应,说明眼睛真的很疲劳了,少玩手机,少看屏幕,做做眼保健啥的。
    58 天前
    回复了 season8 创建的主题 程序员 办公室不开灯,倍感舒适,我是夜猫吗?
    @fkdtz 观察者模式?观察小姐姐还是领导😄?
    58 天前
    回复了 season8 创建的主题 程序员 办公室不开灯,倍感舒适,我是夜猫吗?
    @dxfree 我关过一次,引得万众瞩目,后来一想,可能有的人不睡或者晚睡需要开灯,就没关了,现在都是把外套盖头上睡,明显睡得香一些
    59 天前
    回复了 season8 创建的主题 程序员 办公室不开灯,倍感舒适,我是夜猫吗?
    ![测试图]( https://pic4.zhimg.com/80/v2-a3640506c63d39c8c7231cad164c853f_720w.jpg)
    我用这个图测试的,左右眼分开测试,出现了明显的深浅线条,说是明显长短也算。。
    59 天前
    回复了 season8 创建的主题 程序员 办公室不开灯,倍感舒适,我是夜猫吗?
    @LokiSharp 大佬何许人也?我赶紧去做了下散光测试,真的中了。。
    184 天前
    回复了 season8 创建的主题 问与答 请教一个 Java 多线程嵌套使用的问题
    啊。。评论不支持 md,排版好丑,又有点长,各位见谅。
    184 天前
    回复了 season8 创建的主题 问与答 请教一个 Java 多线程嵌套使用的问题
    @Vedar @1194129822 @lancelee01 @micean 感谢各位的热情解答,我很受启发。再结合朋友给的例子,我仔细读了下源码,已经大致能复盘这个错误了。

    **inner 线程池 reject 的原因:**

    1. 主要原因:队列太小,这里给的是 1,实际每个 outer 线程要产生 3 个任务
    2. 次要原因:outter 线程里面使用 countdownlatch 确实不能起到很好的限流作用,

    **次要原因分析:**
    如 runWorker()源码所示,run 执行完毕并不能代表线程任务执行完毕。这意味着 outter 线程与 inner 线程的空闲线程数可能不是 1:3 的关系。但这里可以通过让 outter 线程 sleep 等待 inner 先执行完成,规避这个因素的影响。规避后,问题还是会存在,说明不是主要原因。

    **主要原因分析:**
    先来看个案例
    ```
    static class MyLinkedBlockingQueue<E> extends LinkedBlockingQueue<E> {
    public MyLinkedBlockingQueue(int capacity) {
    super(capacity);
    }

    @Override
    public boolean offer(E o) {
    System.out.println("任务加入,当前队列数:" + this.size());
    return super.offer(o);
    }
    }

    public static void main(String[] args) throws InterruptedException {
    BlockingQueue queue = new MyLinkedBlockingQueue<>(1);

    // 3 个线程的线程池
    ThreadPoolExecutor taskPoolExecutor = new ThreadPoolExecutor(3, 3, 30, TimeUnit.SECONDS, queue);

    // 先将线程池拉满
    for (int i = 0; i < 3; i++) {
    final int finalI = i;
    taskPoolExecutor.execute(() -> {
    logger.info("{}", finalI);
    });
    }

    // 等待全部任务执行完
    Thread.sleep(1000);

    // 再次执行任务,发现每一个任务都触发加入队列操作。
    for (int i = 10; i < 12; i++) {
    final int finalI = i;
    // 多线程更容易触发 reject
    // new Thread(()-> taskPoolExecutor.execute(() -> logger.info("{}", finalI))).start();
    taskPoolExecutor.execute(() -> logger.info("{}", finalI));
    }
    }
    ```

    执行结果:

    > 23:12:39.988 [pool-1-thread-3] INFO c.r.s.Demo8.lambda$main$0:34 - 2
    23:12:39.988 [pool-1-thread-2] INFO c.r.s.Demo8.lambda$main$0:34 - 1
    23:12:39.988 [pool-1-thread-1] INFO c.r.s.Demo8.lambda$main$0:34 - 0
    任务加入,当前队列数:0
    23:12:40.997 [pool-1-thread-3] INFO c.r.s.Demo8.lambda$null$1:46 - 10
    任务加入,当前队列数:0
    23:12:41.000 [pool-1-thread-2] INFO c.r.s.Demo8.lambda$null$1:46 - 11

    跑完这个案例我感觉我根本不懂线程池,我翻了下源码:
    ```
    public void execute(Runnable command) {
    ...
    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {
    if (addWorker(command, true))
    return;
    c = ctl.get();
    }

    // 线程池满了后,直接不创建核心线程了
    // 这里 isRunning 看的我懵逼,明明任务都执行完了,为啥还是 isRunning,先接受,后面再研究 [1]
    // 然后就触发入队列
    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))
    reject(command);
    ```

    我以为的线程池是:只要有空闲线程,任务是直接丢给线程去执行的。
    **实际情况是:当核心线程数满,不管已有线程是否空闲,任务是先丢到队列,然后空闲线程从队列里面自取。**

    案例中,我给的队列大小是 1,当队列满的时候,会扩容线程池到最大线程池大小到 12,此时如果队列是满的(不管线程是否空闲),继续添加就会 reject 。案例中每组有三个任务,只要线程从队列 take 任务不及时,队列很容易满,从而触发 reject 。

    **验证:**
    1. countDownLatch.await(); 后面加上 sleep,让 outter 线程等 inner 线程结束,排除最开始说的第二个因素的影响。
    2. 将队列改成 3,适当调整线程执行时间(也可以不调),reject 很少触发或不触发。
    3. 将队列改成 9,没有触发 reject

    **总结:**
    1. 这个任务表面是多线程嵌套调用,内外线程调度不确定性导致的线程池问题,其实本质是对线程池理解不对导致线程池滥用的问题。
    2. 任务是添加到队列,空闲线程调用 take()获取,而不是有空闲线程就直接丢到空闲线程(实际任务也难以主动去找空闲线程,还容易造成等待,让线程自取则是生产消费的模式。)
    3. isRunning(c) 这个方法以及相关机制,还要再研究一下。


    再次感谢各位,如有不对的地方,还请指出。。
    185 天前
    回复了 season8 创建的主题 问与答 请教一个 Java 多线程嵌套使用的问题
    @micean
    countDownLatch 不是最后一个 inner 线程执行完成后唤醒 outter 线程吗?那 outter 线程结束应该就意味着有三个 inner 结束。

    而且,尝试过,countDownLatch.await();之后 sleep,也是存在这个问题
    关于   ·   帮助文档   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2001 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 14ms · UTC 04:10 · PVG 12:10 · LAX 21:10 · JFK 00:10
    ♥ Do have faith in what you're doing.