Using AtomicInteger as a static shared counter

In trying to learn about synchronization via Java, I was just messing around with some simple things like making a counter shared between threads.

The problem I am facing is that I cannot figure out how to print the counter sequentially 100% of the time.

int counterValue = this.counter.incrementAndGet();
System.out.println(this.threadName + ": " + counterValue);

      

The given value increments AtomicInteger counter

, gets the new value and prints it to the console indicated by the name of the thread that is responsible for this update. The problem occurs when the method appears to be incrementAndGet()

calling the JVM to switch the context to a different thread to update it before printing the current updated value of the thread. This means that the value is incremented, but not printed, until the thread returns to a running state. This is obvious when looking at this example:

Thread 3: 4034
Thread 3: 4035
Thread 3: 4036
Thread 1: 3944
Thread 1: 4037
Thread 1: 4039
Thread 1: 4040
Thread 2: 3863
Thread 1: 4041
Thread 1: 4043

      

You can see that when execution returns to Thread 1, it prints its value and continues updating. The same can be seen with Thread 2.

I have a feeling that I am missing something very obvious.

+3


source to share


3 answers


The problem occurs when it appears that the incrementAndGet () method calls the JVM to switch the context to another thread to update it before printing the updated value of the current thread

This is a race condition that can often occur in these situations. Even though the counters are AtomicInteger

incrementing correctly, there is nothing to stop Thread 2

from replacing after the increment occurs and before the call println

.

int counterValue = this.counter.incrementAndGet();
// there is nothing stopping a context switch here
System.out.println(this.threadName + ": " + counterValue);

      



If you want to print "counter sequentially 100% of the time" you have to synchronize the lock around the increment and call println

. Of course, if you do, it AtomicInteger

will be wasted.

synchronized (counter) {
    System.out.println(this.threadName + ": " + counter.incrementAndGet());
}

      

If you edit your question to explain why sequential output is needed, there is perhaps a better solution that does not have this race condition.

+7


source


You need to sync the whole construct:

synchronized(this) {    
   int counterValue = this.counter.incrementAndGet();
   System.out.println(this.threadName + ": " + counterValue);
}

      



In this case, you don't need to use AtomicInteger. Normal int

will work (counter ++).

+1


source


To print sequentially, incAndGet and println must be in the critical area, some of the code can only enter one thread, the rest are blocked. Implemented with a binary semaphore like java synchronized

.

You can turn things on your head and one thread will increment the counter and print it out. Other threads can receive only one counter after another in the "critical area". This would be more efficient since critical regions should be kept small and it is preferred not to do I / O.

+1


source







All Articles