Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using AtomicInteger as a static shared counter

In an effort to learn about synchronization via Java, I'm just messing around with some simple things like creating a counter shared between threads.

The problem I've run into is that I can't figure out how to print the counter sequentially 100% of the time.

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

The above increments the AtomicInteger counter, gets the new value, and prints it to the console identified by the thread name that is responsible for that update. The problem occurs when it appears that the incrementAndGet() method is causing the JVM to context switch to another thread for its updates before printing the current thread's updated value. This means that the value gets incremented but not printed until the thread returns to the executing state. This is obvious when looking at this example output:

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 is evident with Thread 2.

I have a feeling that I'm missing something very obvious.

like image 520
Justin Skiles Avatar asked Apr 09 '12 18:04

Justin Skiles


1 Answers

The problem occurs when it appears that the incrementAndGet() method is causing the JVM to context switch to another thread for its updates before printing the current thread's updated value

This is a race condition that often can happen in these situations. Although the AtomicInteger counters are being incremented properly, there is nothing to stop Thread 2 from being swapped out after the increment happens and before the println is called.

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

If you want to print the "counter sequentially 100% of the time" you are going to have to synchronize on a lock around both the increment and the println call. Of course if you do that then the AtomicInteger is wasted.

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

If you edit your question to explain why you need the output to be sequential maybe there is a better solution that doesn't have this race condition.

like image 81
Gray Avatar answered Sep 22 '22 00:09

Gray