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

关于 Java 线程池的一些疑问

  •  1
     
  •   nl101531 ·
    mrdear · 2018-08-08 08:42:30 +08:00 · 3932 次点击
    这是一个创建于 2306 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

    第 1 条附言  ·  2018-08-08 22:01:55 +08:00
    写了测试,假设 qps1000,30 个线程能够保证每个处理在 15ms 以内。

    分成 3 个线程池,每个之中有 10 个线程,那么处理会相当慢,竞争很激烈。因此结论不正确。
    18 条回复    2018-11-07 17:36:23 +08:00
    boywang004
        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...
    });
    }
    ooToo
        2
    ooToo  
       2018-08-08 08:56:10 +08:00 via iPhone
    有问题,竞争锁的是线程和线程,不是线程池和池,而且需要同步的才需要竞争锁。CPU 核心就那么多
    nl101531
        3
    nl101531  
    OP
       2018-08-08 08:58:08 +08:00
    @ooToo 在一个大的线程池里面是那么多线程竞争一个阻塞队列上的锁,而分散开来的话就有了多个锁,每个线程池锁竞争的锁对象是不一样的了。
    szq8014
        4
    szq8014  
       2018-08-08 09:05:17 +08:00   ❤️ 2
    多个小的线程池不还是用锁来同步吗?这句话的重点应该是 “多线程竞争 [锁] 时会引起上下文切换”,所以最好还是不要把数据上锁,不上锁就需要提前把数据分给每个线程,每个线程一亩三分地自己搞自己的。
    hash 取模后不就做到每个线程不需要锁就可以保证数据安全了么~
    nl101531
        5
    nl101531  
    OP
       2018-08-08 09:11:10 +08:00
    @szq8014 大佬这分析有道理,貌似是我理解有问题,作者原意应该只是表达可以用这种做法实现无锁。
    D3EP
        6
    D3EP  
       2018-08-08 09:33:21 +08:00
    @boywang004 偶见天舟 哈哈
    sagaxu
        7
    sagaxu  
       2018-08-08 09:42:19 +08:00 via Android
    如果多个小线程池优于单个大线程池,线程池内部实现的时候做个分段不就好了?
    ioth
        8
    ioth  
       2018-08-08 09:55:35 +08:00
    java 还有效率?指针都不敢有。线程什么的,交给 c 和 c++ 吧。
    yidinghe
        9
    yidinghe  
       2018-08-08 10:31:08 +08:00 via Android
    总的来说,有锁在那里,你怎么摆弄线程池没什么区别的
    WildCat
        10
    WildCat  
       2018-08-08 10:49:56 +08:00
    @ioth 惊了。
    iFlicker
        11
    iFlicker  
       2018-08-08 11:20:18 +08:00
    @ioth 钓鱼么。。。
    AllenTsui
        12
    AllenTsui  
       2018-08-08 11:26:18 +08:00
    @ioth PHP 全宇宙第一 <(* ̄ー ̄)ゞ~
    ioth
        13
    ioth  
       2018-08-09 09:41:20 +08:00
    @AllenTsui 没错,就是一堆$看了脑袋大,是除了 vb 之外 sb 第 2 的语言。
    ioth
        14
    ioth  
       2018-08-09 09:43:13 +08:00
    @WildCat 本来就是做个小电器的接口语言设计,能不垃圾么?还讲 java 的艺术,那 foxpro 还讲优雅?
    所以后来安卓这种山寨货也就用 java 了。
    boywang004
        15
    boywang004  
       2018-08-09 09:59:23 +08:00
    @D3EP =__=,我难道应该再换个 id 才能不被认出来么。
    wocanmei
        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 散列到多个锁上,不同的数据在不同的锁上进行同步,避免了不必要的锁竞争
    nl101531
        17
    nl101531  
    OP
       2018-08-21 12:54:42 +08:00
    @wocanmei 感谢,是应该这样理解
    409474917
        18
    409474917  
       2018-11-07 17:36:23 +08:00
    @ioth 你这个喷子呀,有本事不要用淘宝,淘宝 86%的系统后台都是 java 写的,你也不要用安卓手机,你的家人也不要用安卓手机。看你的这些发言,实际中你应该是个没房没车的屌丝,每个月拿着几千块钱的工资。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2540 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 04:54 · PVG 12:54 · LAX 20:54 · JFK 23:54
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.