在《 Java 并发编程艺术》书中有这样一段话
多线程竞争锁时会引起上下文切换,多余多核处理数据时可以将数据的 ID 按照 Hash 算法取模分段,不同的线程处理不同段的数据。
关于这个我以线程池为例,可以理解为使用一个大的线程池不如使用多个小的线程池,将任务通过 hash 算法分散到不同的线程池中,线程池的 Channel 是阻塞队列实现,高并发抢任务会造成线程阻塞,导致上下文切换,因此这种分散的方法降低单个线程池抢任务的并发量。
这样理解有问题吗?这种做法目前没怎么见过,是真的有效果吗?
1
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... }); } |
2
ooToo 2018-08-08 08:56:10 +08:00 via iPhone
有问题,竞争锁的是线程和线程,不是线程池和池,而且需要同步的才需要竞争锁。CPU 核心就那么多
|
3
nl101531 OP @ooToo 在一个大的线程池里面是那么多线程竞争一个阻塞队列上的锁,而分散开来的话就有了多个锁,每个线程池锁竞争的锁对象是不一样的了。
|
4
szq8014 2018-08-08 09:05:17 +08:00 2
多个小的线程池不还是用锁来同步吗?这句话的重点应该是 “多线程竞争 [锁] 时会引起上下文切换”,所以最好还是不要把数据上锁,不上锁就需要提前把数据分给每个线程,每个线程一亩三分地自己搞自己的。
hash 取模后不就做到每个线程不需要锁就可以保证数据安全了么~ |
6
D3EP 2018-08-08 09:33:21 +08:00
@boywang004 偶见天舟 哈哈
|
7
sagaxu 2018-08-08 09:42:19 +08:00 via Android
如果多个小线程池优于单个大线程池,线程池内部实现的时候做个分段不就好了?
|
8
ioth 2018-08-08 09:55:35 +08:00
java 还有效率?指针都不敢有。线程什么的,交给 c 和 c++ 吧。
|
9
yidinghe 2018-08-08 10:31:08 +08:00 via Android
总的来说,有锁在那里,你怎么摆弄线程池没什么区别的
|
14
ioth 2018-08-09 09:43:13 +08:00
@WildCat 本来就是做个小电器的接口语言设计,能不垃圾么?还讲 java 的艺术,那 foxpro 还讲优雅?
所以后来安卓这种山寨货也就用 java 了。 |
15
boywang004 2018-08-09 09:59:23 +08:00
@D3EP =__=,我难道应该再换个 id 才能不被认出来么。
|
16
wocanmei 2018-08-21 12:46:04 +08:00 1
没有看过 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 散列到多个锁上,不同的数据在不同的锁上进行同步,避免了不必要的锁竞争 |