Home > Back-end >  Java unexpected concurrent result
Java unexpected concurrent result

Time:04-14

While testing concurrency, I found something unexpected.

Concurrency was controlled using concurrentHashMap and AtomicLong.

public class HumanRepository {

    private final static Map<Long, Human> STORE = new ConcurrentHashMap<>();
    private AtomicLong sequence = new AtomicLong();

    public void save(Human human) {
        STORE.put(sequence.incrementAndGet(), human);
    }

    public int size() {
        return STORE.size();
    }


    public Long getSeq() {
        return sequence.get();
    }
}

I tested saving in multiple threads.

    @Test
    void name() throws NoSuchMethodException, InterruptedException {
        final int threads = 3_500;
        final ExecutorService es = Executors.newFixedThreadPool(threads);
        final CountDownLatch count = new CountDownLatch(threads);
        final HumanRepository repository = new HumanRepository();

        for (int i = 0; i < threads; i  ) {
            try {
                es.execute(() -> repository.save(new Human("aa")));
            } finally {
                count.countDown();
            }
        }

        count.await();

        System.out.println("seq = "   repository.getSeq());
        System.out.println("size = "   repository.size());
    }

I tested it with 3500 threads simultaneously. The result I expected is 3500 for both seq and size.

But sometimes I get seq=3499, size=3500. That's weird. It is strange that seq does not come out as 3500, and even though the size is 3500, it does not make sense that seq is 3499.

I don't know why the data number and seq in the map are not the same and 3500 is not coming out.

** If you do Thread.sleep(400L); after count.await();, surprisingly, the value of seq is 3500

enter image description here

CodePudding user response:

You are not actually waiting for all tasks to complete. Which means that if you get the 3500/3500 output, it's by chance.

Specifically, you decrease the countdown latch on the main thread after scheduling the job, instead of inside of the job, once it's done. That means your countdownlatch is basically just another glorified loop variable that doesn't do any inter-thread communication. Try something like this instead:

    for (int i = 0; i < threads; i  ) {
        es.execute(() -> {
          repository.save(new Human("aa"));
          count.countDown();
        });
    }

CodePudding user response:

You are calling count.countDown() outside the thread executing the HumanRepository.save(). So its possible that the main thread is not synchronized for the completion of the threads.

So you may see the results of repository.getSeq() while one thread is running. Can you try with the following code?

        final int threads = 3_500;
        final ExecutorService es = Executors.newFixedThreadPool(threads);
        final CountDownLatch count = new CountDownLatch(threads);
        final HumanRepository repository = new HumanRepository();

        for (int i = 0; i < threads; i  ) {
            try {
                es.execute(() -> {
                    repository.save(new Human("aa"));
                    count.countDown();
                });
            } finally {
                
            }
        }

        count.await();

        System.out.println("seq = "   repository.getSeq());
        System.out.println("size = "   repository.size());
  • Related