Java ConcurrentHashMap.put()是否始终是线程安全的?如果是这样的话,为什么它不能正常工作?

Java ConcurrentHashMap.put()是否始终是线程安全的?如果是这样的话,为什么它不能正常工作?,java,data-structures,collections,concurrency,concurrenthashmap,Java,Data Structures,Collections,Concurrency,Concurrenthashmap,我编写了一个类(userRepository),其中包含一个名为init()的方法。 init方法初始化ConcurrentHashMap,当我在多个线程中调用此方法时(例如在三个线程中以及在每个线程中调用n次),我希望映射的大小等于nx3。但事实并非如此! 另一方面,当我在方法的签名上使用ReentrantLock或synchronized关键字时,它工作得非常好。我的意思是地图的大小等于nx3。 请检查以下示例: public class UserRepository implements

我编写了一个类(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;
    }
}
正如您可以看到的,n=5000000,有3个线程,所以我希望池的大小等于5000000*3=15000000,但它是13991739

我想知道这场冲突的原因是什么


请注意,当我放置synchronize关键字或使用ReentrantLock时,它工作正常

问题不在于哈希映射本身,而在于索引。您使用的是++,它不是原子命令,因此不是线程安全的。当您在函数上使用synchronized word时,它也会同步索引并解决问题。

ConcurrentHashMap.put()
本身是线程安全的(如果多个线程同时调用它,它不会出错),但它当然不能使您自己的周围代码成为线程安全的(例如,递增
index
等).实际上,我尝试了AtomicInteger而不是int,但是没有任何改变,仍然以错误的方式工作,但是你是对的,整个方法不是线程安全的,那么解决方案是什么?如果我们应该使用synchronized关键字或ReentrantLock,我们可以使用HashMap而不是ConcurrentHashMap两个独立的“threadsafe变量”一起使用不会构成线程安全的方法/程序。@GaëlJ谢谢,我的错误在于ConcurrentHashMap依赖于另一个非线程安全的变量,而不是int,它应该可以正常工作,对吗?你是对的,index++不是线程安全的,但是,我将它改为AtomicInteger,并且仍然以错误的方式工作。不仅仅是这样,问题在于两个线程之间的索引。你需要每次为每个线程锁定它,并且只有在创建用户并推进索引之后,然后才释放锁,没有锁,你可以将用户添加到地图,而不需要是并发的,所以你的意思是,正如我在问题中提到的,我必须以某种方式锁定它,通过添加同步或通过锁定/释放机制。谢谢,将ConcurrentHashMap依赖于另一个非线程安全的变量是我的错误。
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```