• 一个由CountDownLatch引发的Bug


    最近恢复Venus的工作,恰逢十一假期尾声,突发一个Bug,让人匪夷所依。我们今天快速倒序追踪,层层回顾。

    1. 背景

    Venus交易中需要并行开数十至百个线程分别快速计算,程序中使用CountDownLatch用于计数,因为之后后做其他处理分析,需要等待所有交易结束。

    程序运行稳定正常,线上也已经0.16版本(个人线上),恰逢十一假期突然出问题,CountDownLatch无法归零,导致整个主线程Hang在那里。如何破?


    2. 场景分析

    我们直接看一下源代码如何:


    平铺直叙,代码中定义了CountDownLatch, 设定其数量为股票个数,然后线程中进行countDown.


    平时运行正常不过,十一为何出问题?难道Venus想让笔者家里加班?


    3. CountDownLanch


    查看log问题主要出现在最终CountDownLatch无法归零,按说我们都把countDown放到finally里面了,应该不会出什么问题了。

    Anyway,我们先看一下CountDownLatch吧。

     

    API中当调用await时候,调用线程处于等待挂起状态,直至count变成0再继续。

    其大体原理如下:

    Java官网中给出的事例代码如下:


    与我们的类似,除了多了个开始计时器,这个应该不是问题吧?官网如下的例子也说明了我们的猜测。


    4. 深入研究

    我们的焦点目光转移至await。

    await
    public boolean await(long timeout, TimeUnit unit)
    throws InterruptedException
    使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。如果当前计数为零,则此方法立刻返回true 值。

    如果当前计数大于零,则出于线程调度目的,将禁用当前线程,且在发生以下三种情况之一前,该线程将一直处于休眠状态:

    • 由于调用 countDown() 方法,计数到达零;或者
    • 其他某个线程中断当前线程;或者
    • 已超出指定的等待时间。


    上述API详细描述,有几个疑点值得进一步推敲,首先我们的count并未到达0,所以应该是线程调用中出现了问题,难道是有一些没有捕获的异常?可是我们是在finally做的countdown啊?

    把所有捕获异常都换成Thowable, 无用!


    网上有人云:

    Executors.newFixedThreadPool这是个有固定活动线程数。当提交到池中的任务数大于固定活动线程数时,任务就会放到阻塞队列中等待。
    主任务和子任务都放到了线程池中,就有下面的情况发生,
    主任务调用一次CountDownLatch实例的await()方法时,当前线程就会一直占用一个活动线程,如果多次调用,那么就会一直占用多个活动线程,如果调用次数大于固定活动线程数,那么就可能造成阻塞队列中某些子任务一直不被执行,CountDownLatch实例的countDown()的方法一直不被调用,那么对应的主任务所在线程就会无限等待,与死锁现像一样

    我们的newFixedThreadPool是大了点,直接50-100多个,而我的mac只有2核(可怜啊),跟这个有关系么?

    我们把FixedThreadPool改成了默认cpu的核数,或者hard code成2,运行,无效,hang,再说以前也是50-100个,运行没有问题啊。

    解决办法是最好不要用CountDownLatch实例的await(),归避长时间阻塞线程的风险,任何多线程应用程序都有死锁风险,改用CountDownLatch实例的await(long timeout, TimeUnit unit),设定超时时间,如果超时,

    多线程程序确实有死锁风险, 当然安全起见可以给定超时时间。回到我们这里不解决问题啊,到底问题出在哪里?


    5. 真相大白

    问题到这里,我们把该怀疑的地方都怀疑了一遍,还是不得要领。难道问题出在某一股票交易中?恰逢十一假期,有关连?


    好吧,只好使出大招,就是二分排查法,哈哈。说白了就是笨办法,逐一排查,股票由100个到50十个,到40,到20,正常。难道真是跟线程数量有关系?

    不科学啊。

    反过来,把剩下的80个来运行一下,居然正常通过。


    嗯,到此,感觉就快找到问题了。问题就出现在那20个股票当中的某些有问题。继续排查,终于定位到600188,这么好的代码,有问题?


    好吧,至此可以进入debug了。

    终于的终于,问题定位到该股票在十一前恰好处罚交易策略,而逢十一假期,代码中一段计算nextTradingDate的算法有点问题(其实是粘贴错了变量),直接导致死循环!!!

    好吧,那个高手没有写过死循环啊?

    一秒钟改掉后,Venus瞬间触发100个线程,愉悦的开始交易!

    总结


    此问题虽然最终并非有并发导致的问题,而然其隐藏之深也与并发分不开;并发多线程是好东西,当然也要慎用,做到知其然知其所以然。



    原文链接:https://blog.csdn.net/erixhao/article/details/52747955

  • 相关阅读:
    springcloud的turbine集成zookeeper
    点赞功能与redis的相遇
    kmeans聚类源代码
    java map.entry
    M2Eclipse:Maven Eclipse插件无法搜索远程库的解决方法
    Maven仓库
    java生成验证码
    手工利用Chrome浏览器“Javascript控制台”
    分类算法之朴素贝叶斯分类(Naive Bayesian classification)
    JavaWeb 服务启动时,在后台启动加载一个线程
  • 原文地址:https://www.cnblogs.com/erichi101/p/16423996.html
Copyright © 2020-2023  润新知