简体   繁体   中英

Why are my threads giving me this output when accessing a synchronized method?

I'm trying to experiment on multithreading and synchronization, so I have created this simple object that is shared between all threads:

public class SharedObject {

    private int count = 0;

    public synchronized int getCount(){
        return count;
    }

    public synchronized void incrementCount(){
        count++;
    }
}

And it's accessed by 3 threads in this manner:

public static void main(String[] args) throws Exception {


    SharedObject sharedObject = new SharedObject();
    ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);

    Runnable task = () -> {
        for(int i = 0; i < 10; i++){

            System.out.println("Thread : " + Thread.currentThread().getName() 
            + " count : " + sharedObject.getCount());

            sharedObject.incrementCount();

            try{
                Thread.currentThread().sleep(2000);
            }
            catch (Exception e){}
        }
    };

    executor.submit(task);
    executor.submit(task);
    executor.submit(task);

    executor.shutdown();
    executor.awaitTermination(1, TimeUnit.HOURS);

    System.out.println("Final : " + sharedObject.getCount());
}

My output is the following :

Thread : pool-1-thread-2 count : 0
Thread : pool-1-thread-1 count : 0
Thread : pool-1-thread-3 count : 0
Thread : pool-1-thread-3 count : 3
Thread : pool-1-thread-2 count : 3
Thread : pool-1-thread-1 count : 3
Thread : pool-1-thread-2 count : 6
Thread : pool-1-thread-1 count : 6
Thread : pool-1-thread-3 count : 6
...

If my understanding is correct (please correct me if i's wrong), this is happening because:

  1. The first thread calls getCount() , gets the lock on the method, and as soon as he prints the count value, releases the lock which is then acquired by the second thread to call getCount() , and onward with the last thread

  2. When all 3 threads are done calling getCount() , each of them is now calling incrementCount() and since the method is synchronized, each thread sees the updated value before incrementing the count, which explains why we see jumps of +3 in the output

  3. As soon as a thread finishes , it calls sleep(2000) on itself, but since the calls are so fast, it seems like the three threads start and stop to sleep at the same time

However , when i remove sleep(2000) , i get the following output:

Thread : pool-1-thread-3 count : 0
Thread : pool-1-thread-2 count : 0
Thread : pool-1-thread-1 count : 0
Thread : pool-1-thread-2 count : 2
Thread : pool-1-thread-3 count : 1
Thread : pool-1-thread-2 count : 4
Thread : pool-1-thread-1 count : 3
Thread : pool-1-thread-2 count : 6
Thread : pool-1-thread-3 count : 5
Thread : pool-1-thread-2 count : 8
Thread : pool-1-thread-1 count : 7
Thread : pool-1-thread-2 count : 10
Thread : pool-1-thread-3 count : 9
Thread : pool-1-thread-2 count : 12
Thread : pool-1-thread-1 count : 11
Thread : pool-1-thread-2 count : 14

And i don't understand how this can happen. For example, how can thread-3 see the count being equal to 1 if thread-2 saw it equal to 2 before him and incremented it ?

Any explanation would be appreciated to help me better understand Java in a multithreaded synchronized environment. Thank you for your time.

Remember that the ouput on your screen might not have anything to do with the order of execution of getCount()/incrementCount(), there's no locking in the code that prints the output.

For example, how can thread-3 see the count being equal to 1 if thread-2 saw it equal to 2 before him and incremented it ?

This output can occur if you have these order of execution:

  1. Thread-3 calls getCount() and it returns 1.
  2. Thread-1 calls incrementCount()
  3. Thread-2 calls getCount() and it returns 2.
  4. Thread-2 calls System.out.println that prints: "pool-1-thread-2 count : 2"
  5. Thread-3 calls System.out.println that prints: "pool-1-thread-3 count : 1"

Just because a thread reads a value before another one doesn't mean it will print it before the other one. Yo would need the read and the print to be done atomically, inside the synchronized block, to have that guarantee.

So what you can have is thus

  • thread 3 reads and prints the value (0): Thread : pool-1-thread-3 count : 0

  • thread 2 reads and prints the value (0): Thread : pool-1-thread-2 count : 0

  • thread 1 reads and print the value (0): Thread : pool-1-thread-1 count : 0

  • thread 3 increments the value (1)

  • thread 3 reads the value (1)
  • thread 2 increments the value (2)
  • thread 2 reads and prints the value (2): Thread : pool-1-thread-2 count : 2
  • thread 3 prints the value it has read before: Thread : pool-1-thread-3 count : 1

In your SharedObject , the individual getCount and incrementCount methods are synchronized, but nothing prevents all three threads from calling getCount (one at a time) before any of them calls incrementCount . And then again after each sleep. That's what your first output is showing.

Without the sleep() it is furthermore possible for for one thread to call getCount() more than once before one or more of the others calls incrementCount() even once. Technically it's not forbidden even with the sleep. Likewise, it is possible for one thread to get, increment, and print getween when another gets and when it prints. These kinds of orderings explain why your output without the sleep is not sequential.

If you want to see sequential output without skips, then you need more broadly-scoped synchronization. For example, in your task implementation you might use a synchronized block:

            synchronized (sharedObject) {
                System.out.println("Thread : " + Thread.currentThread().getName() 
                        + " count : " + sharedObject.getCount());

                sharedObject.incrementCount();
            }

Each time a thread enter s the synchronized block, it will get the count, print it, and increment it without any of the other threads doing any of those things in between.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM