在一个同事关于并行流的问题之后,我编写了以下代码来测试某些东西。
public class Test {
public static void main(String args[]) {
List<Runnable> list = new LinkedList<>();
list.add(() -> {
try {
Thread.sleep(10000);
System.out.println("Time : " + System.nanoTime() + " " + "Slow task");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
for (int i = 0; i < 1000; i++) {
int j = i;
list.add(() -> System.out.println("Time : " + System.nanoTime() + " " + j));
}
list.parallelStream().forEach(r -> r.run());
}
}
奇怪的是,输出始终类似于以下内容。
Time : 4096118049370412 61
Time : 4096118049567530 311
Time : 4096118049480238 217
Time : 4096118049652415 405
Time : 4096118049370678 436
Time : 4096118049370575 155
Time : 4096118049720639 437
Time : 4096118049719368 280
Time : 4096118049804630 281
Time : 4096118049684148 406
Time : 4096118049660398 218
TRUNCATED
Time : 4096118070511768 669
Time : 4096118070675678 670
Time : 4096118070584951 426
Time : 4096118070704143 427
Time : 4096118070714441 428
Time : 4096118070722080 429
Time : 4096118070729569 430
Time : 4096118070736782 431
Time : 4096118070744069 432
Time : 4096118070751286 433
Time : 4096118070758554 434
Time : 4096118070765913 435
Time : 4096118070550370 930
Time : 4096118070800538 931
Time : 4096118070687425 671
Time : 4096118070813669 932
Time : 4096118070827794 672
Time : 4096118070866089 933
Time : 4096118070881358 673
Time : 4096118070895344 934
Time : 4096118070907608 674
Time : 4096118070920712 935
Time : 4096118070932934 675
Time : 4096118070945131 936
Time : 4096118070957850 676
Time : 4096118070982326 677
Time : 4096118070991158 678
Time : 4096118070999002 679
Time : 4096118071006501 680
Time : 4096118071017766 681
Time : 4096118071025766 682
Time : 4096118071033318 683
Time : 4096118071070603 684
Time : 4096118071080240 685
Time : 4096128063025914 Slow task
Time : 4096128063123940 0
Time : 4096128063148135 1
Time : 4096128063173285 2
Time : 4096128063176723 3
Time : 4096128063179939 4
Time : 4096128063183077 5
Time : 4096128063191001 6
Time : 4096128063194156 7
Time : 4096128063197273 8
Time : 4096128063200395 9
Time : 4096128063203581 10
Time : 4096128063206988 11
Time : 4096128063210155 12
Time : 4096128063213285 13
Time : 4096128063216411 14
Time : 4096128063219542 15
Time : 4096128063222733 16
Time : 4096128063232190 17
Time : 4096128063235653 18
Time : 4096128063238827 19
Time : 4096128063241962 20
Time : 4096128063245176 21
Time : 4096128063248296 22
Time : 4096128063251444 23
Time : 4096128063254557 24
Time : 4096128063257705 25
Time : 4096128063261566 26
Time : 4096128063264733 27
Time : 4096128063268115 28
Time : 4096128063272851 29
Process finished with exit code 0
也就是说,即使所有其他任务都已完成,总会有一些任务在等待慢速任务完成处理。我假设慢速任务应该只占用一个线程,而其他所有任务应该都没有问题地完成,并且只有慢速任务应该占用整整10秒钟。我有8个CPU,所以并行度为7。
这可能是什么原因?
要添加更多信息,该代码仅用于理解目的。我不会把它放在生产中的任何地方。
答案 0 :(得分:2)
There are some limited capabilities when it comes to work-stealing with streams,因此,如果一个线程将自己钉在其他跑步者的某些工作上,则该工作将被阻塞,直到完成其他任务的处理为止。
您可以通过在代码周围添加更多少量调试说明来实现此目的...
class Test {
public static void main(String[] args) {
List<Runnable> list = new LinkedList<>();
list.add(() -> {
try {
System.out.println("Long sleep - " + Thread.currentThread().getName());
Thread.sleep(10000);
System.out.println("Time : " + System.nanoTime() + " " + "Slow task");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
for (int i = 0; i < 1000; i++) {
int j = i;
list.add(() -> System.out.println("Time : " + System.nanoTime() + " " + j));
}
list.parallelStream().forEach(r -> {
System.out.println(Thread.currentThread().getName());
r.run();
System.out.println();
});
}
}
运行此命令后,我看到以下消息:
Long sleep - ForkJoinPool.commonPool-worker-4
...大约十秒钟后...
Time : 11525122027429 Slow task
ForkJoinPool.commonPool-worker-4
Time : 11525122204035 0
ForkJoinPool.commonPool-worker-4
Time : 11525122245739 1
ForkJoinPool.commonPool-worker-4
Time : 11525122267015 2
ForkJoinPool.commonPool-worker-4
Time : 11525122286921 3
ForkJoinPool.commonPool-worker-4
Time : 11525122306266 4
ForkJoinPool.commonPool-worker-4
Time : 11525122338787 5
ForkJoinPool.commonPool-worker-4
Time : 11525122357288 6
ForkJoinPool.commonPool-worker-4
Time : 11525122376716 7
ForkJoinPool.commonPool-worker-4
Time : 11525122395218 8
ForkJoinPool.commonPool-worker-4
Time : 11525122414165 9
ForkJoinPool.commonPool-worker-4
Time : 11525122432755 10
ForkJoinPool.commonPool-worker-4
Time : 11525122452805 11
ForkJoinPool.commonPool-worker-4
Time : 11525122472624 12
ForkJoinPool.commonPool-worker-4
Time : 11525122491380 13
ForkJoinPool.commonPool-worker-4
Time : 11525122514417 14
ForkJoinPool.commonPool-worker-4
Time : 11525122534550 15
ForkJoinPool.commonPool-worker-4
Time : 11525122553751 16
因此,这意味着在我的盒子上,worker-4计划进行一些工作,基于存在一些不均匀块的事实,该工作无法被盗。注意:如果线程正在分块地处理任务,则该工作将不会进一步分解。
[31, 31, 31, 32, 31, 31, 31, 32, 31, 31, 31, 32, 31, 31, 31, 32, 31, 31, 31, 32, 31, 31, 31, 32, 31, 31, 31, 32, 31, 32, 31, 32, 0]
如果您正在寻找可以从运行时间更长的线程中窃取工作的线程实现,则最好直接使用工作窃取池。
class Test {
public static void main(String[] args) throws InterruptedException {
List<Runnable> list = new LinkedList<>();
list.add(() -> {
try {
System.out.println("Long sleep - " + Thread.currentThread().getName());
Thread.sleep(10000);
System.out.println("Time : " + System.nanoTime() + " " + "Slow task");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
for (int i = 0; i < 1000; i++) {
int j = i;
list.add(() -> {
System.out.println(Thread.currentThread().getName());
System.out.println("Time : " + System.nanoTime() + " " + j);
System.out.println();
});
}
final ExecutorService stealingPool = Executors.newWorkStealingPool();
list.forEach(stealingPool::execute);
stealingPool.shutdown();
stealingPool.awaitTermination(15, TimeUnit.SECONDS);
}
}
以上内容在列表末尾显示了更稳定,更合理的结果:
Time : 12210445469314 Slow task
...。这意味着所有可用的工作已在分配的时间(15秒)内处理完毕。