【问题标题】:Behaviour of Threads during blocking IO in Java ForkJoinPoolJava ForkJoinPool 中阻塞 IO 期间线程的行为
【发布时间】:2022-01-18 20:00:01
【问题描述】:

如果 ForkJoinPool 中的线程执行阻塞 I/O 活动(在下面的代码中使用 Thread.sleep(10000) 模拟),它应该选择另一个不会导致阻塞 IO 的任务。 但是,使用 ForkJoinPool 以 1 或 2 的并行度运行以下代码,结果与它不匹配。线程不会放弃阻塞任务(即任务 1 和任务 2)并移动到非阻塞任务(任务 3)。 在 pool size =1 的情况下,所有任务按顺序执行,而我希望 Task 3 首先完成,因为它在两种情况下都是非阻塞的( pool size=1 或 2)

在阻塞 IO 的情况下,ForkJoinPool 中线程的实际行为是什么?

    ForkJoinPool pool = new  ForkJoinPool(1);
    
    List<Future<String>> tasks = new ArrayList<Future<String>>();
    

    //Blocking
    tasks.add(pool.submit(() ->  {  Thread.sleep(10000); System.out.println("Task 1 woke up"+ Thread.currentThread().isDaemon()); return "task1"; }));
    //Blocking
    tasks.add(pool.submit(() ->  { Thread.sleep(10000); System.out.println("Task 2 woke up"+Thread.currentThread().isDaemon()); return "task2"; }));
    //Non Blocking
    tasks.add(pool.submit(() ->  {  System.out.println("Task 3 Runs"+Thread.currentThread().isDaemon()); return "task3"; }));
    

    
    int i=0;
    System.out.println("pool size = " + pool.getParallelism() + "Thread count=" +pool.getPoolSize() + "Stealing =" + pool.getStealCount());
    System.out.println("waiting");
    
    String str1 = tasks.get(0).get();
    String str2 = tasks.get(1).get();
    String str3 = tasks.get(2).get();
    
    System.out.println("Results = " +  str1+ str2 +str3);
    
    System.out.println("done");

结果:

//池大小 = 2
任务 2 已唤醒真实
任务 1 已唤醒真实
任务 3 运行为真

//池大小=1
任务 1 已唤醒真实
任务 2 已唤醒真实
任务 3 运行为真

另一个观察:
当我们首先提交任务 3(非阻塞)时,只有它首先被调用。

【问题讨论】:

  • 什么版本的Java?就像您希望实施纤维一样?
  • 嗨,Nathan,它的 Java 15。我希望 ForkJoinPool 中的线程在当前分配的任务执行一些 IO 时切换到非阻塞任务。根据 Project Loom ,ForkJoinPool 将用于 Fibers,因此期望它会以这种方式运行。 cr.openjdk.java.net/~rpressler/loom/Loom-Proposal.html

标签: java multithreading executorservice thread-sleep forkjoinpool


【解决方案1】:

如果 ForkJoinPool 中的线程执行阻塞 I/O 活动(在下面的代码中使用 Thread.sleep(10000) 模拟),它应该选择另一个不会导致阻塞 IO 的任务。

这不是线程的行为方式。一个线程代表一个正在进行的执行。不能随意跳转到另一个Runnable。线程无法知道线程是阻塞 I/O 还是执行密集计算,除非线程引擎和特定 I/O 是为此设计的(例如 jdbc 与 r2dbc)。

换句话说,如果您使用 Reactor/Spring Reactive/其他带有传统阻塞 I/O 的响应式编程框架,例如 JDBC,您的线程仍将挂起等待 I/O。

阻塞 I/O 通常在轮询套接字时实现为循环,特定 I/O 之外的任何东西都无法知道这实际上是阻塞 I/O。

据我所知,ForkJoinPool 不是响应式的,它会将 Runnables 交给线程,仅此而已。

【讨论】:

    【解决方案2】:

    ForkJoinPool 仅用作 Loom 提案中提到的调度程序。只有虚拟线程有能力放弃阻塞任务并切换到另一个任务。根据我在 loom 邮件列表中阅读邮件的记忆,loom 团队目前不打算重新设计 java.util.concurrent 的 ForkJoinPool 以使用虚拟线程。

    【讨论】:

    • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
    猜你喜欢
    • 1970-01-01
    • 2020-11-10
    • 1970-01-01
    • 1970-01-01
    • 2012-09-03
    • 1970-01-01
    • 2015-06-25
    • 2013-10-05
    • 2014-01-20
    相关资源
    最近更新 更多