Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is i++ not atomic?

Why is i++ not atomic in Java?

To get a bit deeper in Java I tried to count how often the loop in threads are executed.

So I used a

private static int total = 0; 

in the main class.

I have two threads.

  • Thread 1: Prints System.out.println("Hello from Thread 1!");
  • Thread 2: Prints System.out.println("Hello from Thread 2!");

And I count the lines printed by thread 1 and thread 2. But the lines of thread 1 + lines of thread 2 don't match the total number of lines printed out.

Here is my code:

import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Level; import java.util.logging.Logger;  public class Test {      private static int total = 0;     private static int countT1 = 0;     private static int countT2 = 0;     private boolean run = true;      public Test() {         ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();         newCachedThreadPool.execute(t1);         newCachedThreadPool.execute(t2);         try {             Thread.sleep(1000);         }         catch (InterruptedException ex) {             Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);         }         run = false;         try {             Thread.sleep(1000);         }         catch (InterruptedException ex) {             Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);         }         System.out.println((countT1 + countT2 + " == " + total));     }      private Runnable t1 = new Runnable() {         @Override         public void run() {             while (run) {                 total++;                 countT1++;                 System.out.println("Hello #" + countT1 + " from Thread 2! Total hello: " + total);             }         }     };      private Runnable t2 = new Runnable() {         @Override         public void run() {             while (run) {                 total++;                 countT2++;                 System.out.println("Hello #" + countT2 + " from Thread 2! Total hello: " + total);             }         }     };      public static void main(String[] args) {         new Test();     } } 
like image 721
Andie2302 Avatar asked Aug 06 '14 18:08

Andie2302


People also ask

What does not atomic mean?

a : not relating to, concerned with, or composed of atoms Gerald Cleaver, professor and graduate program director in Baylorʼs department of physics, will present "Life on the Landscape," which will consider the place of the Earthʼs universe and the possibility of nonatomic-based (intelligent) life forms outside of it. ...

Why long and double are not atomic in Java?

It's not atomic because it's a multiple-step operation at the machine code level. That is, longs and doubles are longer than the processor's word length.

Is ++ an operation atomic?

On objects without an atomic type, standard never defines ++ as an atomic operation.

Is increment operation an atomic?

The increment-memory machine instruction on an X86 is atomic only if you use it with a LOCK prefix. x++ in C and C++ doesn't have atomic behavior.


2 Answers

i++ involves two operations :

  1. read the current value of i
  2. increment the value and assign it to i

When two threads perform i++ on the same variable at the same time, they may both get the same current value of i, and then increment and set it to i+1, so you'll get a single incrementation instead of two.

Example :

int i = 5; Thread 1 : i++;            // reads value 5 Thread 2 : i++;            // reads value 5 Thread 1 : // increments i to 6 Thread 2 : // increments i to 6            // i == 6 instead of 7 
like image 36
Eran Avatar answered Sep 27 '22 20:09

Eran


i++ is probably not atomic in Java because atomicity is a special requirement which is not present in the majority of the uses of i++. That requirement has a significant overhead: there is a large cost in making an increment operation atomic; it involves synchronization at both the software and hardware levels that need not be present in an ordinary increment.

You could make the argument that i++ should have been designed and documented as specifically performing an atomic increment, so that a non-atomic increment is performed using i = i + 1. However, this would break the "cultural compatibility" between Java, and C and C++. As well, it would take away a convenient notation which programmers familiar with C-like languages take for granted, giving it a special meaning that applies only in limited circumstances.

Basic C or C++ code like for (i = 0; i < LIMIT; i++) would translate into Java as for (i = 0; i < LIMIT; i = i + 1); because it would be inappropriate to use the atomic i++. What's worse, programmers coming from C or other C-like languages to Java would use i++ anyway, resulting in unnecessary use of atomic instructions.

Even at the machine instruction set level, an increment type operation is usually not atomic for performance reasons. In x86, a special instruction "lock prefix" must be used to make the inc instruction atomic: for the same reasons as above. If inc were always atomic, it would never be used when a non-atomic inc is required; programmers and compilers would generate code that loads, adds 1 and stores, because it would be way faster.

In some instruction set architectures, there is no atomic inc or perhaps no inc at all; to do an atomic inc on MIPS, you have to write a software loop which uses the ll and sc: load-linked, and store-conditional. Load-linked reads the word, and store-conditional stores the new value if the word has not changed, or else it fails (which is detected and causes a re-try).

like image 150
Kaz Avatar answered Sep 27 '22 18:09

Kaz