ConcurrentHashMap.put() 总是线程安全的吗?如果是这样,那么为什么它不能正常工作?
is ConcurrentHashMap.put() always Thread-Safe? if so, then why it is not working correctly?
我写了一个 class (userRepository),包括一个名为 init() 的方法。
init 方法初始化 ConcurrentHashMap,当我在多个线程中调用此方法时(例如在三个线程中并且在每个线程中调用 n 次),我希望映射的大小等于 nx3。但事实并非如此!
另一方面,当我在方法的签名上使用 ReentrantLock 或 synchronized 关键字时,它工作得很好。我的意思是地图的大小等于 nx3。
请检查以下示例:
public class UserRepository implements CustomRepository<User, Integer> {
private final Map<Integer, User> userMap = new ConcurrentHashMap<>();
private int index = 0;
public void init() {
userMap.put(index, new User("User_" + index).setId(index));
index++;
}
public List<User> findAll() {
List<User> userList = new ArrayList<>();
for (Integer id : userMap.keySet())
userList.add(userMap.get(id));
return userList;
}
}
public class ChallengeApplication {
static ExecutorService ex = Executors.newFixedThreadPool(2);
public static void main(String[] args) {
UserRepository userRepository = new UserRepository();
int a = 5000000;
long start = System.currentTimeMillis();
ex.submit(() -> {
for (int j = 0; j < a; j++)
userRepository.init();
long time = System.currentTimeMillis() - start;
System.out.println(Thread.currentThread() + " finished in " + time);
});
ex.submit(() -> {
for (int j = 0; j < a; j++)
userRepository.init();
long time = System.currentTimeMillis() - start;
System.out.println(Thread.currentThread() + " finished in " + time);
});
for (int j = 0; j < a; j++)
userRepository.init();
long time = System.currentTimeMillis() - start;
System.out.println(Thread.currentThread() + " finished in " + time);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Size of map: " + userRepository.findAll().size());
}
}
这是控制台输出:
Thread[pool-1-thread-2,5,main] finished in 3832
Thread[main,5,main] finished in 3938
Thread[pool-1-thread-1,5,main] finished in 3996
Size of map: 14347920```
如您所见,n=5000000 并且有 3 个线程,所以我预计池的大小等于 5000000*3= 15000000,但它是 13991739!
我想知道这个冲突是什么原因造成的!?
请注意,当我输入同步关键字或使用 ReentrantLock 时,它可以正常工作
问题不在于哈希映射本身,而在于索引。您使用的 ++ 不是原子命令,因此不是线程安全的。当您在函数上使用同步字时,它也会同步索引并解决问题。
我写了一个 class (userRepository),包括一个名为 init() 的方法。 init 方法初始化 ConcurrentHashMap,当我在多个线程中调用此方法时(例如在三个线程中并且在每个线程中调用 n 次),我希望映射的大小等于 nx3。但事实并非如此! 另一方面,当我在方法的签名上使用 ReentrantLock 或 synchronized 关键字时,它工作得很好。我的意思是地图的大小等于 nx3。 请检查以下示例:
public class UserRepository implements CustomRepository<User, Integer> {
private final Map<Integer, User> userMap = new ConcurrentHashMap<>();
private int index = 0;
public void init() {
userMap.put(index, new User("User_" + index).setId(index));
index++;
}
public List<User> findAll() {
List<User> userList = new ArrayList<>();
for (Integer id : userMap.keySet())
userList.add(userMap.get(id));
return userList;
}
}
public class ChallengeApplication {
static ExecutorService ex = Executors.newFixedThreadPool(2);
public static void main(String[] args) {
UserRepository userRepository = new UserRepository();
int a = 5000000;
long start = System.currentTimeMillis();
ex.submit(() -> {
for (int j = 0; j < a; j++)
userRepository.init();
long time = System.currentTimeMillis() - start;
System.out.println(Thread.currentThread() + " finished in " + time);
});
ex.submit(() -> {
for (int j = 0; j < a; j++)
userRepository.init();
long time = System.currentTimeMillis() - start;
System.out.println(Thread.currentThread() + " finished in " + time);
});
for (int j = 0; j < a; j++)
userRepository.init();
long time = System.currentTimeMillis() - start;
System.out.println(Thread.currentThread() + " finished in " + time);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Size of map: " + userRepository.findAll().size());
}
}
这是控制台输出:
Thread[pool-1-thread-2,5,main] finished in 3832
Thread[main,5,main] finished in 3938
Thread[pool-1-thread-1,5,main] finished in 3996
Size of map: 14347920```
如您所见,n=5000000 并且有 3 个线程,所以我预计池的大小等于 5000000*3= 15000000,但它是 13991739!
我想知道这个冲突是什么原因造成的!?
请注意,当我输入同步关键字或使用 ReentrantLock 时,它可以正常工作
问题不在于哈希映射本身,而在于索引。您使用的 ++ 不是原子命令,因此不是线程安全的。当您在函数上使用同步字时,它也会同步索引并解决问题。