ThreadPoolExecutor 创建线程池
Callable 接口的实现类处理逻辑
在实现类的 call()中,每次都要访问一个资源,这一步要耗时很久。持有这个资源的对象其实是可以复用的,怎么样才可以让线程本身持有这个资源?或者各位大佬给点其他思路
1
Bluelion 2022-09-02 10:05:07 +08:00
ThreadLocal
|
2
beetlerx 2022-09-02 10:05:47 +08:00
那就放 threadLocal 里呗
|
3
L0L 2022-09-02 10:15:42 +08:00
1 、ThreadLocal 直接定义最简单;
2 、或者使用自定义 ThreadFactory ,来自定义线程池内线程,构建时组合一个资源; 3 、我看你的描述,这个对象如果是可以复用的,为什么不通过单例来做呢,简单又高效(注意:没有线程安全的问题情况下)。 |
4
lmshl 2022-09-02 10:20:27 +08:00
放资源池不就行了,学 jdbc 的 connection pool 呗。
|
5
shanghai1943 2022-09-02 10:21:16 +08:00
你是想在单个线程里复用还是线程池里复用?
|
6
abc0123xyz OP @L0L #3 这个对象只能同时处理一个,多个线程同时调用的时候会其他的就要等待了。
现在 call()中每次都新建是因为,这个对象不是很可靠,有时候会出问题,一旦出问题,这个对象可能就用不了了,所以需要销毁重新 new 。 其实我是希望对象本身可以服用,处理过程中一旦抛出异常或超出一定时间,就把对象销毁重新 new |
7
abc0123xyz OP |
8
dqzcwxb 2022-09-02 10:31:22 +08:00
用 Caffeine 做缓存全局共享前提是你的共享对象只读不写,ThreadLocal 存在父子线程传递问题要是你不传递你就得每个线程都去创建
|
9
abc0123xyz OP @shanghai1943 #5
都可以,线程池中复用应该更好 |
10
ForkNMB 2022-09-02 10:32:25 +08:00
FastThreadLocal
|
11
L0L 2022-09-02 10:54:54 +08:00
@abc0123xyz
本质上你这个资源是线程不安全的,你需要同步方法来保证执行过程中的安全;如果是我来写的话,我可能还是会优先使用单例。 * 在获取单例的时候,校验下该对象是否可用; * 如果对象不可用的话会,重新构建一个单例对象出来,替换原本的对象; * 同时执行具体方法的时候,加上同步锁,保证当前只有一个线程执行具体的方法。 实现起来方法多种多样,只要能满足需求,都是好的方法。 |
12
timethinker 2022-09-02 11:09:45 +08:00
这个对象有没有可能创建多个实例,每一个线程使用自己所绑定的那一个?这样线程之间就不用竞争,也无需加锁。
具体的做法就是,在线程池中的其中一个线程执行你的代码的时候: 1 、从 ThreadLocal 取出这个对象。 2 、如果不存在,实例化这个对象并对它进行初始化,然后再保存到 ThreadLocal 里面。 3 、使用这个对象。 这样当下一次这个线程再次执行时,就可以省略掉第二个步骤,对象就可以得到复用。 至于你说的这个对象有可能会损坏,你可以创建一个类来包装并代理委托执行相关的方法,通过封装内部实际对象,就可以对异常进行处理,比如重新构造内部实际对象,这样外部的代码就可以不用关心内部的处理流程。但是如果异常是必须要在业务逻辑中进行处理的,就不能通过代理类对这些异常进行掩盖,以免导致意外的 BUG 。 |
13
xsqfjys 2022-09-02 11:14:46 +08:00
好奇怪的场景,一个不可靠的对象可还行
|
14
hidemyself 2022-09-02 11:22:39 +08:00
apache commons pool2 看一下这个
|
15
DavidDee 2022-09-02 11:32:29 +08:00
比较好奇,这个对象是怎么变为不可靠的呢
|
16
abc0123xyz OP |
17
zmal 2022-09-02 12:02:12 +08:00
这不就是连接池的适用场景么
|
18
ma836323493 2022-09-02 15:09:53 +08:00
看你说的像是多个线程复用, 那就个一个静态 引用 ,然后 用 lock 把线程中使用的地方 lock 起来
|
19
urnoob 2022-09-02 15:22:34 +08:00
可参考 guava cache
所有线程执行的 call 方法中都是从 cache 中取(“读”)这个实例。当没有的时候就,guava cache 会调用你在创建 cache 时定义的加载这个实例的方法。剩下的就是做好线程同步即可 避免重复加载即可。 |
20
goldenAndGreen 2022-09-02 15:22:34 +08:00
apache object pool, 线程从池子里取对象
|
21
nothingistrue 2022-09-02 15:57:35 +08:00
@abc0123xyz 你要多个线程对同一个对象互斥,又要这个对象依赖的第三方对象能被多个线程复用。这是要一个并发场景下的共享对象,一部分状态是隔离的,另一部分状态是公用的,这是不可能的。
狗日的,我看你的描述头疼的很。 首先,你到底有没有这样的数据或者业务要求:最多一个线程处理它,如果是多个线程同时处理的时候,必须排队。如果没有的话,就别提在关注这一点。我看你的业务场景压根不涉及并发加锁。 |
22
nothingistrue 2022-09-02 16:12:57 +08:00
提醒你一点:你提交给执 Executors 的是一个实现 Callable 接口的对象,不是 Callable 类的对象,这个对象不只有 call() 方法,是可以定义其他字段的。
所以你没必要在 call() 方法中去生成或申请那个资源,你可以在你实现 Callable 的类(别再用匿名类了)上定义一个字段来映射那个资源,然后在提交到 Executors 前就给它设值。这个设值过程,就跟线程池或异步执行器无关了,就是典型的单例模式。 上面不会解决你想要的超时处理。 |
23
nothingistrue 2022-09-02 16:24:00 +08:00
超时那个问题,把 future.get() ,换成:
try { future.get(long timeout, TimeUnit unit); } catch (TimeoutException e){ // 超时时候的处理,这里你想咋处理就咋处理,但是当前这个任务肯定是失败了。 } |
24
corningsun 2022-09-02 17:00:32 +08:00
@abc0123xyz 重点描述下你的第三方工具到底是啥吧?什么协议,支持多少并发? QPS 之类的有限制吗?
卡死的原因要找出来。 然后再根据 kafka 数据量有多少,决定单线程跑,还是起多线程,要不要加限速 |
25
chenshun00 2022-09-02 18:47:24 +08:00
为啥第三方卡死了,你就把对象丢了呢,在这里做个熔断就好了吧,不就可以保持这个一直可用了么。
|