Ehsan
Ehsan

Reputation: 78

is ConcurrentHashMap.put() always Thread-Safe? if so, then why it is not working correctly?

I wrote a class (userRepository) including a method named init(). init method initializes ConcurrentHashMap, when I invoke this method in multiple threads (for example in three threads and in each thread for n times), I expect that the size of the map equals nx3. but it is not! on the other hand, when I use ReentrantLock or synchronized keyword on the signature of the method it works perfectly fine. I mean the size of the map is equal to nx3. please check the following example:

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());
    }
}

and this is the console output:

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```

as you can see n=5000000 and there are 3 threads, so I expect that size of the pool equals 5000000*3= 15000000 but it is 13991739!

I would like to know what is the reason for this conflict!?

please note that when I put synchronize keyword or using ReentrantLock it works correctly

Upvotes: 0

Views: 259

Answers (1)

SgtOmer
SgtOmer

Reputation: 215

The problem is not with the hash map itself but the index. You are using ++ which isn’t an atomic command and thus isn’t thread safe. When you are using synchronized word on the function it syncs the index too and solves the problem.

Upvotes: 1

Related Questions