关于 Java 线程池的一些疑问

2018-08-08 08:42:30 +08:00
 nl101531

在《 Java 并发编程艺术》书中有这样一段话

多线程竞争锁时会引起上下文切换,多余多核处理数据时可以将数据的 ID 按照 Hash 算法取模分段,不同的线程处理不同段的数据。

关于这个我以线程池为例,可以理解为使用一个大的线程池不如使用多个小的线程池,将任务通过 hash 算法分散到不同的线程池中,线程池的 Channel 是阻塞队列实现,高并发抢任务会造成线程阻塞,导致上下文切换,因此这种分散的方法降低单个线程池抢任务的并发量。

这样理解有问题吗?这种做法目前没怎么见过,是真的有效果吗?

3928 次点击
所在节点    Java
18 条回复
boywang004
2018-08-08 08:54:01 +08:00
如果任务的耗时方差比较大,有个别耗时较长,可能会造成部分线程池饥饿。
简单就这样的代码,但是更多是为了解决按照特定数据进行串行化的……
Map<Integer, Executor> executorPool;
void init() {
executorPool = IntStream.range(0, 10).boxed().collect(collectingAndThen(toMap(identity(), it -> Executors.newXxxx(..))), Collections::unmodifiableMap);
}

void foo(SomePojo pojo) {
executorPool.get(pojo.getSomeField().hashCode() % executorPool.size()).execute(() -> {
// do something...
});
}
ooToo
2018-08-08 08:56:10 +08:00
有问题,竞争锁的是线程和线程,不是线程池和池,而且需要同步的才需要竞争锁。CPU 核心就那么多
nl101531
2018-08-08 08:58:08 +08:00
@ooToo 在一个大的线程池里面是那么多线程竞争一个阻塞队列上的锁,而分散开来的话就有了多个锁,每个线程池锁竞争的锁对象是不一样的了。
szq8014
2018-08-08 09:05:17 +08:00
多个小的线程池不还是用锁来同步吗?这句话的重点应该是 “多线程竞争 [锁] 时会引起上下文切换”,所以最好还是不要把数据上锁,不上锁就需要提前把数据分给每个线程,每个线程一亩三分地自己搞自己的。
hash 取模后不就做到每个线程不需要锁就可以保证数据安全了么~
nl101531
2018-08-08 09:11:10 +08:00
@szq8014 大佬这分析有道理,貌似是我理解有问题,作者原意应该只是表达可以用这种做法实现无锁。
D3EP
2018-08-08 09:33:21 +08:00
@boywang004 偶见天舟 哈哈
sagaxu
2018-08-08 09:42:19 +08:00
如果多个小线程池优于单个大线程池,线程池内部实现的时候做个分段不就好了?
ioth
2018-08-08 09:55:35 +08:00
java 还有效率?指针都不敢有。线程什么的,交给 c 和 c++ 吧。
yidinghe
2018-08-08 10:31:08 +08:00
总的来说,有锁在那里,你怎么摆弄线程池没什么区别的
WildCat
2018-08-08 10:49:56 +08:00
@ioth 惊了。
iFlicker
2018-08-08 11:20:18 +08:00
@ioth 钓鱼么。。。
AllenTsui
2018-08-08 11:26:18 +08:00
@ioth PHP 全宇宙第一 <(* ̄ー ̄)ゞ~
ioth
2018-08-09 09:41:20 +08:00
@AllenTsui 没错,就是一堆$看了脑袋大,是除了 vb 之外 sb 第 2 的语言。
ioth
2018-08-09 09:43:13 +08:00
@WildCat 本来就是做个小电器的接口语言设计,能不垃圾么?还讲 java 的艺术,那 foxpro 还讲优雅?
所以后来安卓这种山寨货也就用 java 了。
boywang004
2018-08-09 09:59:23 +08:00
@D3EP =__=,我难道应该再换个 id 才能不被认出来么。
wocanmei
2018-08-21 12:46:04 +08:00
没有看过 Java 并发编程艺术,但看起来这里说的应该是锁分段而不是线程池,比如

```java
public void synchronizeOneLock(int uid) {
synchronized (this) {

// ...
}
}

private final Object[] locks;

public void synchronizeMultiLock(int uid) {
synchronized (locks[uid % locks.length]) {

// ...
}
}
```

假设用户可以用 uid 表示,synchronizeOneLock 会将所有用户在 this 这一个锁上同步,而 synchronizeMultiLock 则会根据 uid 散列到多个锁上,不同的数据在不同的锁上进行同步,避免了不必要的锁竞争
nl101531
2018-08-21 12:54:42 +08:00
@wocanmei 感谢,是应该这样理解
409474917
2018-11-07 17:36:23 +08:00
@ioth 你这个喷子呀,有本事不要用淘宝,淘宝 86%的系统后台都是 java 写的,你也不要用安卓手机,你的家人也不要用安卓手机。看你的这些发言,实际中你应该是个没房没车的屌丝,每个月拿着几千块钱的工资。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/477804

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX