求教一个 Java 线程池的问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
abc0123xyz
V2EX    Java

求教一个 Java 线程池的问题

  •  
  •   abc0123xyz Sep 2, 2022 3986 views
    This topic created in 1333 days ago, the information mentioned may be changed or developed.

    ThreadPoolExecutor 创建线程池
    Callable 接口的实现类处理逻辑

    在实现类的 call()中,每次都要访问一个资源,这一步要耗时很久。持有这个资源的对象其实是可以复用的,怎么样才可以让线程本身持有这个资源?或者各位大佬给点其他思路

    Supplement 1    Sep 2, 2022
    补充一下
    整个处理逻辑是,从 mq 获取信息,然后丢入线程池处理,整个处理过程创建这个对象要 2s ,让该对象处理资源要 1s+
    然后通过 future.get()获取处理结果

    怎么样才可以让对象创建后可以被重复使用,并且处理过程中一旦抛出异常或超出一定时间就销毁重新 new
    25 replies    2022-09-02 18:47:24 +08:00
    Bluelion
        1
    Bluelion  
       Sep 2, 2022
    ThreadLocal
    beetlerx
        2
    beetlerx  
       Sep 2, 2022
    那就放 threadLocal 里呗
    L0L
        3
    L0L  
       Sep 2, 2022
    1 、ThreadLocal 直接定义最简单;
    2 、或者使用自定义 ThreadFactory ,来自定义线程池内线程,构建时组合一个资源;
    3 、我看你的描述,这个对象如果是可以复用的,为什么不通过单例来做呢,简单又高效(注意:没有线程安全的问题情况下)。
    lmshl
        4
    lmshl  
       Sep 2, 2022
    放资源池不就行了,学 jdbc 的 connection pool 呗。
    shanghai1943
        5
    shanghai1943  
       Sep 2, 2022
    你是想在单个线程里复用还是线程池里复用?
    abc0123xyz
        6
    abc0123xyz  
    OP
       Sep 2, 2022
    @L0L #3 这个对象只能同时处理一个,多个线程同时调用的时候会其他的就要等待了。

    现在 call()中每次都新建是因为,这个对象不是很可靠,有时候会出问题,一旦出问题,这个对象可能就用不了了,所以需要销毁重新 new 。

    其实我是希望对象本身可以服用,处理过程中一旦抛出异常或超出一定时间,就把对象销毁重新 new
    abc0123xyz
        7
    abc0123xyz  
    OP
       Sep 2, 2022
    @lmshl #4
    @Bluelion #1
    @beetlerx #2
    感谢各位大佬
    我研究一下,平时一直都在增删改查,没处理过这种东西
    dqzcwxb
        8
    dqzcwxb  
       Sep 2, 2022
    用 Caffeine 做缓存全局共享前提是你的共享对象只读不写,ThreadLocal 存在父子线程传递问题要是你不传递你就得每个线程都去创建
    abc0123xyz
        9
    abc0123xyz  
    OP
       Sep 2, 2022
    @shanghai1943 #5
    都可以,线程池中复用应该更好
    ForkNMB
        10
    ForkNMB  
       Sep 2, 2022
    FastThreadLocal
    L0L
        11
    L0L  
       Sep 2, 2022
    @abc0123xyz
    本质上你这个资源是线程不安全的,你需要同步方法来保证执行过程中的安全;如果是我来写的话,我可能还是会优先使用单例。
    * 在获取单例的时候,校验下该对象是否可用;
    * 如果对象不可用的话会,重新构建一个单例对象出来,替换原本的对象;
    * 同时执行具体方法的时候,加上同步锁,保证当前只有一个线程执行具体的方法。

    实现起来方法多种多样,只要能满足需求,都是好的方法。
    timethinker
        12
    timethinker  
       Sep 2, 2022
    这个对象有没有可能创建多个实例,每一个线程使用自己所绑定的那一个?这样线程之间就不用竞争,也无需加锁。

    具体的做法就是,在线程池中的其中一个线程执行你的代码的时候:
    1 、从 ThreadLocal 取出这个对象。
    2 、如果不存在,实例化这个对象并对它进行初始化,然后再保存到 ThreadLocal 里面。
    3 、使用这个对象。

    这样当下一次这个线程再次执行时,就可以省略掉第二个步骤,对象就可以得到复用。

    至于你说的这个对象有可能会损坏,你可以创建一个类来包装并代理委托执行相关的方法,通过封装内部实际对象,就可以对异常进行处理,比如重新构造内部实际对象,这样外部的代码就可以不用关心内部的处理流程。但是如果异常是必须要在业务逻辑中进行处理的,就不能通过代理类对这些异常进行掩盖,以免导致意外的 BUG 。
    xsqfjys
        13
    xsqfjys  
       Sep 2, 2022
    好奇怪的场景,一个不可靠的对象可还行
    hidemyself
        14
    hidemyself  
       Sep 2, 2022
    apache commons pool2 看一下这个
    DavidDee
        15
    DavidDee  
       Sep 2, 2022
    比较好奇,这个对象是怎么变为不可靠的呢
    abc0123xyz
        16
    abc0123xyz  
    OP
       Sep 2, 2022
    @xsqfjys #13
    @DavidDee #15
    其实就 java 调用第三方工具,第三方卡死,所以这个对象就没用了,要重新 new 一个
    zmal
        17
    zmal  
       Sep 2, 2022
    这不就是连接池的适用场景么
    adimn
        18
    adimn  
       Sep 2, 2022
    看你说的像是多个线程复用, 那就个一个静态 引用 ,然后 用 lock 把线程中使用的地方 lock 起来
    urnoob
        19
    urnoob  
       Sep 2, 2022
    可参考 guava cache
    所有线程执行的 call 方法中都是从 cache 中取(“读”)这个实例。当没有的时候就,guava cache 会调用你在创建 cache 时定义的加载这个实例的方法。剩下的就是做好线程同步即可 避免重复加载即可。
    goldenAndGreen
        20
    goldenAndGreen  
       Sep 2, 2022
    apache object pool, 线程从池子里取对象
    nothingistrue
        21
    nothingistrue  
       Sep 2, 2022
    @abc0123xyz 你要多个线程对同一个对象互斥,又要这个对象依赖的第三方对象能被多个线程复用。这是要一个并发场景下的共享对象,一部分状态是隔离的,另一部分状态是公用的,这是不可能的。

    狗日的,我看你的描述头疼的很。

    首先,你到底有没有这样的数据或者业务要求:最多一个线程处理它,如果是多个线程同时处理的时候,必须排队。如果没有的话,就别提在关注这一点。我看你的业务场景压根不涉及并发加锁。
    nothingistrue
        22
    nothingistrue  
       Sep 2, 2022
    提醒你一点:你提交给执 Executors 的是一个实现 Callable 接口的对象,不是 Callable 类的对象,这个对象不只有 call() 方法,是可以定义其他字段的。

    所以你没必要在 call() 方法中去生成或申请那个资源,你可以在你实现 Callable 的类(别再用匿名类了)上定义一个字段来映射那个资源,然后在提交到 Executors 前就给它设值。这个设值过程,就跟线程池或异步执行器无关了,就是典型的单例模式。

    上面不会解决你想要的超时处理。
    nothingistrue
        23
    nothingistrue  
       Sep 2, 2022
    超时那个问题,把 future.get() ,换成:
    try {
    future.get(long timeout, TimeUnit unit);
    } catch (TimeoutException e){
    // 超时时候的处理,这里你想咋处理就咋处理,但是当前这个任务肯定是失败了。
    }
    corningsun
        24
    corningsun  
       Sep 2, 2022
    @abc0123xyz 重点描述下你的第三方工具到底是啥吧?什么协议,支持多少并发? QPS 之类的有限制吗?

    卡死的原因要找出来。

    然后再根据 kafka 数据量有多少,决定单线程跑,还是起多线程,要不要加限速
    chenshun00
        25
    chenshun00  
       Sep 2, 2022
    为啥第三方卡死了,你就把对象丢了呢,在这里做个熔断就好了吧,不就可以保持这个一直可用了么。
    About     Help     Advertise     Blog     API     FAQ     Solana     3378 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 65ms UTC 12:40 PVG 20:40 LAX 05:40 JFK 08:40
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86