【发布时间】:2021-12-01 21:58:58
【问题描述】:
当我运行以下代码时,8 个可用线程中只有 2 个运行,谁能解释为什么会这样?如何更改代码以利用所有 8 个线程?
Tree.java:
package il.co.roy;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class Tree<T>
{
private final T data;
private final Set<Tree<T>> subTrees;
public Tree(T data, Set<Tree<T>> subTrees)
{
this.data = data;
this.subTrees = subTrees;
}
public Tree(T data)
{
this(data, new HashSet<>());
}
public Tree()
{
this(null);
}
public T getData()
{
return data;
}
public Set<Tree<T>> getSubTrees()
{
return subTrees;
}
@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Tree<?> tree = (Tree<?>) o;
return Objects.equals(data, tree.data) &&
Objects.equals(subTrees, tree.subTrees);
}
@Override
public int hashCode()
{
return Objects.hash(data, subTrees);
}
@Override
public String toString()
{
return "Tree{" +
"data=" + data +
", subTrees=" + subTrees +
'}';
}
public void sendCommandAll()
{
if (data != null)
System.out.println("[" + Thread.currentThread().getName() + "] sending command to " + data);
try
{
Thread.sleep(5000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
if (data != null)
System.out.println("[" + Thread.currentThread().getName() + "] tree with data " + data + " got " + true);
subTrees.parallelStream()
// .map(Tree::sendCommandAll)
.forEach(Tree::sendCommandAll);
// .reduce(true, (aBoolean, aBoolean2) -> aBoolean && aBoolean2);
}
}
(不管我用forEach还是reduce)。
Main.java:
package il.co.roy;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Main
{
public static void main(String... args)
{
System.out.println("Processors: " + Runtime.getRuntime().availableProcessors());
final Tree<Integer> root = new Tree<>(null,
Set.of(new Tree<>(1,
IntStream.range(2, 7)
.boxed()
.map(Tree::new)
.collect(Collectors.toSet()))));
root.sendCommandAll();
// IntStream.generate(() -> 1)
// .parallel()
// .forEach(i ->
// {
// System.out.println(Thread.currentThread().getName());
// try
// {
// Thread.sleep(5000);
// } catch (InterruptedException e)
// {
// e.printStackTrace();
// }
// });
}
}
在main 方法中,我创建了一个具有以下结构的树:\
root (data is `null`)
|- 1
|- 2
|- 3
|- 4
|- 5
|- 6
sendCommandAll 函数处理每个子树(并行)只有当它的父完成处理时。
但结果如下:
处理器:8
[main] 向 1 发送命令
[主] 数据 1 的树得到了 true
[main] 向 6 发送命令
[ForkJoinPool.commonPool-worker-2] 向 5 发送命令
[main] 数据 6 的树得到了 true
[ForkJoinPool.commonPool-worker-2] 数据为 5 的树为 true
[ForkJoinPool.commonPool-worker-2] 向 4 发送命令
[ForkJoinPool.commonPool-worker-2] 数据 4 的树得到了 true
[ForkJoinPool.commonPool-worker-2] 向 3 发送命令
[ForkJoinPool.commonPool-worker-2] 数据 3 的树得到了 true
[ForkJoinPool.commonPool-worker-2] 向 2 发送命令
[ForkJoinPool.commonPool-worker-2] 数据为 2 的树为真
(作为记录,当我执行Main.java 中的注释代码时,JVM 使用了所有可用的 7(+1)个线程commonPool)
如何改进我的代码?
【问题讨论】:
-
我不认为我遇到了同样的问题:首先我使用JDK 17,即使我使用自定义
ForkJoinPool,并行度为20,只有2个线程处于活动状态 -
正如答案所说 - 它不能保证工作。此外,如果我重写您的代码以使用 List 而不是 Set 我会看到池中使用了更多线程。
-
正如this answer 的后半部分所解释的,
HashMaps(进而HashSets)具有少量元素,与它们的(默认)容量相比可能会分配它们的工作糟糕,取决于哈希码分布。您可以使用new ArrayList<>(subTrees).parallelStream()解决此问题,但您的方法还有其他缺陷,例如在开始遍历孩子之前完成工作/等待。您应该将迭代逻辑与实际操作分开。 -
谢谢@Holger,它确实解决了我的问题,您能否将您的评论改写为获得积分和积分的官方答案:-)
标签: java multithreading java-8 java-stream java-17