使用具有多个线程的 ConcurrentHashMap 的不同结果 Java
Different results using ConcurrentHashMap with multiple Threads Java
我将 main 中的字符串列表拆分为 2 个不同的线程以映射其中的单词。
每次执行此代码时,我都会得到不同的映射结果。
要么我有一个很大的逻辑缺陷,要么我在线程和并发集合方面遗漏了一些东西。
任何人都可以理解为什么会这样吗?
列表中添加了 8 个“a”和 6 个“b”。
P.S。如果我只使用一个线程,这不会发生!
编辑 1
将 map.put() 更改为 map.merge(word, 1, Integer::sum),仍然无效
编辑 2
以下解决方案我没有使用 if/else,仅合并并且按预期工作。
public class MyThread extends Thread {
private List<String> list;
private final ConcurrentHashMap<String, Integer> map;
public MyThread(ConcurrentHashMap<String, Integer> map, List<String> list) {
this.map = map;
this.list = list;
}
@Override
public void run() {
for (String word : list){
map.merge(word, 1, Integer::sum);
}
}
public ConcurrentHashMap<String, Integer> getMap() {
return map;
}
}
public static void main(String[] args) throws InterruptedException {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
List<String> list = new ArrayList<>();
list.add("a");list.add("a");list.add("a");list.add("a");list.add("b");list.add("b");list.add("b");
list.add("a");list.add("a");list.add("a");list.add("a");list.add("b");list.add("b");list.add("b");
MyThread[] ts = new MyThread[2];
int start = 0;
int end = list.size()/2;
for (int i = 0; i < 2; i++){
ts[i] = new MyThread(map,new ArrayList<>(list.subList(start, end)));
ts[i].start();
start = end;
end = list.size();
}
for (int i = 0; i < 2; i++){
ts[i].join();
}
for(String word : map.keySet()){
System.out.println("Key = " + word + ". Value = " + map.get(word));
}
}
首先 - 您应该使用 map.containsKey(word)
而不是 map.contains(word)
。
关于一些运行后的不同结果。调用 containsKey()
和 merge()
方法时没有原子代码。它们在原子上是分开的,但不是原子上在一起的。
解决方案只是更改您的代码并仅调用原子的 merge()
。您根本不需要 if-else
部分。
@Override
public void run() {
for (String word : list) {
map.merge(word, 1, Integer::sum);
}
}
我将 main 中的字符串列表拆分为 2 个不同的线程以映射其中的单词。
每次执行此代码时,我都会得到不同的映射结果。
要么我有一个很大的逻辑缺陷,要么我在线程和并发集合方面遗漏了一些东西。
任何人都可以理解为什么会这样吗?
列表中添加了 8 个“a”和 6 个“b”。
P.S。如果我只使用一个线程,这不会发生!
编辑 1
将 map.put() 更改为 map.merge(word, 1, Integer::sum),仍然无效
编辑 2
以下解决方案我没有使用 if/else,仅合并并且按预期工作。
public class MyThread extends Thread {
private List<String> list;
private final ConcurrentHashMap<String, Integer> map;
public MyThread(ConcurrentHashMap<String, Integer> map, List<String> list) {
this.map = map;
this.list = list;
}
@Override
public void run() {
for (String word : list){
map.merge(word, 1, Integer::sum);
}
}
public ConcurrentHashMap<String, Integer> getMap() {
return map;
}
}
public static void main(String[] args) throws InterruptedException {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
List<String> list = new ArrayList<>();
list.add("a");list.add("a");list.add("a");list.add("a");list.add("b");list.add("b");list.add("b");
list.add("a");list.add("a");list.add("a");list.add("a");list.add("b");list.add("b");list.add("b");
MyThread[] ts = new MyThread[2];
int start = 0;
int end = list.size()/2;
for (int i = 0; i < 2; i++){
ts[i] = new MyThread(map,new ArrayList<>(list.subList(start, end)));
ts[i].start();
start = end;
end = list.size();
}
for (int i = 0; i < 2; i++){
ts[i].join();
}
for(String word : map.keySet()){
System.out.println("Key = " + word + ". Value = " + map.get(word));
}
}
首先 - 您应该使用 map.containsKey(word)
而不是 map.contains(word)
。
关于一些运行后的不同结果。调用 containsKey()
和 merge()
方法时没有原子代码。它们在原子上是分开的,但不是原子上在一起的。
解决方案只是更改您的代码并仅调用原子的 merge()
。您根本不需要 if-else
部分。
@Override
public void run() {
for (String word : list) {
map.merge(word, 1, Integer::sum);
}
}