JDK17 的 ThreadPoolExecutor 不会在 execut(runnable) 后立即执行,这是什么新特性吗? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
yodhcn
V2EX    Java

JDK17 的 ThreadPoolExecutor 不会在 execut(runnable) 后立即执行,这是什么新特性吗?

  •  
  •   yodhcn
    yodhcn 2023-05-22 14:59:41 +08:00 2486 次点击
    这是一个建于 883 天前的主题,其中的信息可能已经有所发展或是发生改变。

    用 IDEA 新建了两个测试项目,一个 JDK8 ,另一个 JDK17

    测试代码如下:

    public class Task implements Runnable { private int no; private int size; public Task(int no, int size) { this.no = no; this.size = size; } @Override public void run() { try { System.out.println("执行中, Task: " + no + ", Thread: " + Thread.currentThread().getName() + ", queue.size: " + size); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } 
    public class Main { private static ExecutorService pool; public static void main(String[] args) { BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(8); pool = new ThreadPoolExecutor( 2, 5, 1000, TimeUnit.MILLISECONDS, queue, Executors.defaultThreadFactory(), new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { System.out.println("拒绝中, " + r.toString() + ", queue.size: " + queue.size()); } }); for (int i = 0; i < 15; i++) { pool.execute(new Task(i, queue.size())); } pool.shutdown(); } } 

    JDK 8 环境下的运行结果:

    执行中, Task: 1, Thread: pool-1-thread-2, queue.size: 0 执行中, Task: 10, Thread: pool-1-thread-3, queue.size: 8 执行中, Task: 11, Thread: pool-1-thread-4, queue.size: 8 执行中, Task: 12, Thread: pool-1-thread-5, queue.size: 8 执行中, Task: 0, Thread: pool-1-thread-1, queue.size: 0 拒绝中, com.example.demo.Task@6d6f6e28, queue.size: 8 拒绝中, com.example.demo.Task@135fbaa4, queue.size: 8 执行中, Task: 2, Thread: pool-1-thread-4, queue.size: 0 执行中, Task: 3, Thread: pool-1-thread-3, queue.size: 1 执行中, Task: 4, Thread: pool-1-thread-1, queue.size: 2 执行中, Task: 5, Thread: pool-1-thread-2, queue.size: 3 执行中, Task: 6, Thread: pool-1-thread-5, queue.size: 4 执行中, Task: 7, Thread: pool-1-thread-4, queue.size: 5 执行中, Task: 9, Thread: pool-1-thread-3, queue.size: 7 执行中, Task: 8, Thread: pool-1-thread-1, queue.size: 6 

    JDK 17 环境下的运行结果:

    拒绝中, demo.Task@2d98a335, queue.size: 8 拒绝中, demo.Task@4e50df2e, queue.size: 8 执行中, Task: 11, Thread: pool-1-thread-4, queue.size: 8 执行中, Task: 10, Thread: pool-1-thread-3, queue.size: 8 执行中, Task: 12, Thread: pool-1-thread-5, queue.size: 8 执行中, Task: 0, Thread: pool-1-thread-1, queue.size: 0 执行中, Task: 1, Thread: pool-1-thread-2, queue.size: 0 执行中, Task: 3, Thread: pool-1-thread-4, queue.size: 1 执行中, Task: 2, Thread: pool-1-thread-3, queue.size: 0 执行中, Task: 4, Thread: pool-1-thread-5, queue.size: 2 执行中, Task: 5, Thread: pool-1-thread-2, queue.size: 3 执行中, Task: 6, Thread: pool-1-thread-1, queue.size: 4 执行中, Task: 7, Thread: pool-1-thread-4, queue.size: 5 执行中, Task: 8, Thread: pool-1-thread-3, queue.size: 6 执行中, Task: 9, Thread: pool-1-thread-2, queue.size: 7 

    结论

    可见 JDK17 的 ThreadPoolExecutor ,在通过 execut 提交 runnable 后,不会立即执行被提交的 runnable ,而是等待一段时间。如果在这段等待时间内没有新的 runnable 提交,才开始执行。

    10 条回复    2023-05-23 11:13:34 +08:00
    hankli
        1
    hankli  
       2023-05-22 16:06:11 +08:00
    把 jdk17 的 tpe 复制一份,在 runWorker 方法加个日志,你会发现立即执行了.并没有等待.从执行 runworker 到真正直行你 task 的 run 中间时间有点变长了

    2(阶段):pool-1-thread-3Task{no=8, time=168958}
    2:pool-1-thread-4Task{no=9, time=194958}
    拒绝中, Task{no=11, time=-19455630845391}, queue.size: 8
    2:pool-1-thread-5Task{no=10, time=194047}
    3(阶段):pool-1-thread-3Task{no=8, time=394249}
    2:pool-1-thread-2Task{no=1, time=614927}
    3:pool-1-thread-5Task{no=10, time=350700}
    拒绝中, Task{no=12, time=-19455631020344}, queue.size: 8
    3:pool-1-thread-4Task{no=9, time=333429}
    执行中, Task: 10, Thread: pool-1-thread-5, queue.size: 8
    执行中, Task: 8, Thread: pool-1-thread-3, queue.size: 8
    yodhcn
        2
    yodhcn  
    OP
       2023-05-22 16:31:08 +08:00
    @hankli #1 谢谢老哥的指导。

    我之后又做了个实验,写了个死循环一直调用 pool.execute(new Task(i, queue.size()))

    [假设] 不会立即执行被提交的 runnable ,而是等待一段时间。如果在这段等待时间内没有新的 runnable 提交,才开始执行。
    [实验] 写死循环一直调用 pool.execute(new Task(i, queue.size())),不断提交新的 runnable ,如果假设成立,被提交的 runnable 将永远不会被执行。
    为了方便观察,注释掉 RejectedExecutionHandler 里的打印语句,结果在控制台发现 “执行中, Task...” 日志,与假设矛盾。

    正如老哥所说的那样,只是 “从执行 runworker 到真正直行你 task 的 run 中间时间有点变长了”
    cheneydog
        3
    cheneydog  
       2023-05-22 19:48:06 +08:00   1
    所以结论是啥?为啥时间还变长了?有啥优势?
    blessingsi
        4
    blessingsi  
       2023-05-22 20:52:01 +08:00
    java8:
    ```
    执行中, Task: 0, Thread: pool-1-thread-1, queue.size: 0, time: 425747251451497
    执行中, Task: 1, Thread: pool-1-thread-2, queue.size: 0, time: 425747251525194
    执行中, Task: 10, Thread: pool-1-thread-3, queue.size: 8, time: 425747251772510
    执行中, Task: 11, Thread: pool-1-thread-4, queue.size: 8, time: 425747251892745
    执行中, Task: 12, Thread: pool-1-thread-5, queue.size: 8, time: 425747251928019
    拒绝中, Task@4554617c, queue.size: 8, time: 425747251889215
    拒绝中, Task@74a14482, queue.size: 8, time: 425747252008173
    执行中, Task: 2, Thread: pool-1-thread-3, queue.size: 0, time: 425749253444220
    执行中, Task: 3, Thread: pool-1-thread-1, queue.size: 1, time: 425749254108044
    执行中, Task: 4, Thread: pool-1-thread-4, queue.size: 2, time: 425749254203373
    执行中, Task: 5, Thread: pool-1-thread-5, queue.size: 3, time: 425749254286334
    执行中, Task: 6, Thread: pool-1-thread-2, queue.size: 4, time: 425749254359863
    执行中, Task: 7, Thread: pool-1-thread-3, queue.size: 5, time: 425751254069256
    执行中, Task: 8, Thread: pool-1-thread-2, queue.size: 6, time: 425751259174650
    执行中, Task: 9, Thread: pool-1-thread-4, queue.size: 7, time: 425751259199900
    ```

    java17:
    ```
    拒绝中, Task@30f39991, queue.size: 8, time: 425953431226281
    拒绝中, Task@38af3868, queue.size: 8, time: 425953447958506
    执行中, Task: 10, Thread: pool-1-thread-3, queue.size: 8, time: 425953430891601
    执行中, Task: 11, Thread: pool-1-thread-4, queue.size: 8, time: 425953431206209
    执行中, Task: 1, Thread: pool-1-thread-2, queue.size: 0, time: 425953430740641
    执行中, Task: 0, Thread: pool-1-thread-1, queue.size: 0, time: 425953430633193
    执行中, Task: 12, Thread: pool-1-thread-5, queue.size: 8, time: 425953431287397
    执行中, Task: 3, Thread: pool-1-thread-2, queue.size: 1, time: 425955450935401
    执行中, Task: 4, Thread: pool-1-thread-5, queue.size: 2, time: 425955450944853
    执行中, Task: 2, Thread: pool-1-thread-4, queue.size: 0, time: 425955450921501
    执行中, Task: 6, Thread: pool-1-thread-3, queue.size: 4, time: 425955451482689
    执行中, Task: 5, Thread: pool-1-thread-1, queue.size: 3, time: 425955451440625
    执行中, Task: 7, Thread: pool-1-thread-2, queue.size: 5, time: 425957453093453
    执行中, Task: 8, Thread: pool-1-thread-5, queue.size: 6, time: 425957453281909
    执行中, Task: 9, Thread: pool-1-thread-4, queue.size: 7, time: 425957453416090
    ```

    实际运行的时间和 sout 看到的顺序是不一样的
    yodhcn
        5
    yodhcn  
    OP
       2023-05-22 21:07:40 +08:00
    @blessingsi #4

    “执行中, Task: 10, Thread: pool-1-thread-3, queue.size: 8, time: 425747251772510”
    日志里的 time 时间戳,指的是在 ThreadPoolExecutor#runWorker 运行开始?还是在 ThreadPoolExecutor#runWorker 方法体里调用 task.run(); 之前?
    kawowa
        6
    kawowa  
       2023-05-23 07:31:45 +08:00 via iPhone
    很有趣的发现,mark 一个
    zhiyu1998
        7
    zhiyu1998  
       2023-05-23 08:53:04 +08:00
    cool~
    blessingsi
        8
    blessingsi  
       2023-05-23 09:28:48 +08:00 via Android   1
    @a href="/member/yodhcn">yodhcn 在 run 方法开始的时候记录一下时间。
    ```
    var now= system.nanotime();
    sour("执行中,task" + "xxxx" + now);
    ```
    liudaolunhuibl
        9
    liudaolunhuibl  
       2023-05-23 10:23:49 +08:00   1
    jdk17 比起 jdk8 在 runworker 中真正执行你 task 的 run 方法之前新增了一个方法:clearInterruptsForTaskRun ,注释是这样的:* Ensures that unless the pool is stopping, the current thread
    * does not have its interrupt set. This requires a double-check
    * of state in case the interrupt was cleared concurrently with a
    * shutdownNow -- if so, the interrupt is re-enabled.
    blessingsi
        10
    blessingsi  
       2023-05-23 11:13:34 +08:00
    看到的打印的顺序和实际方法开始执行的顺序是没有一致性保证的。而且 jdk17 和 jdk8 的 sout 方法实现也变了。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     918 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 21:15 PVG 05:15 LAX 14:15 JFK 17:15
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86