V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
abc0123xyz
V2EX  ›  Java

求教一个 Java 线程池的问题

  •  
  •   abc0123xyz · 278 天前 · 2849 次点击
    这是一个创建于 278 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    卡死的原因要找出来。

    然后再根据 kafka 数据量有多少,决定单线程跑,还是起多线程,要不要加限速
    chenshun00
        25
    chenshun00  
       278 天前
    为啥第三方卡死了,你就把对象丢了呢,在这里做个熔断就好了吧,不就可以保持这个一直可用了么。
    关于   ·   帮助文档   ·   博客   ·   nftychat   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1547 人在线   最高记录 5634   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 47ms · UTC 16:51 · PVG 00:51 · LAX 09:51 · JFK 12:51
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.