Autoincrement In Java

Many of the gurus out there are already aware of this, but the auto-increment and auto-decrement operators are not thread safe in Java. I was working in some code recently where the only mutation that was occurring was an auto-increment call to an index. After wrapping all of the interactions with this _currentIndex variable in synchronized blocks, I looked at the damage and thought, “I wonder if I can just make this volatile and be done with it.”

Using volatile to solve this particular problem would require that the operation be atomic. I wasn’t sure of this, so I thought I’d test it out myself. The first thing I did was create a very simple class so I could take a look at the byte code that was generated.

package so.dahlgren;

public class AutoIncrement {
  int i = 0;

  public void doitPost() {
    int x = i++;
  }

  public void doitPre() {
    int x = ++i;
  }

}

This very simple class just provides methods that wrap the pre and post increment operators. If we take a look at the byte code generated for this class file with javap we can see the issue come leaping out like a four-headed chromatic dragon:

Compiled from "AutoIncrement.java"
public class so.dahlgren.AutoIncrement extends java.lang.Object{
int i;

public so.dahlgren.AutoIncrement();
  Code:
    0: aload_0
    1:  invokespecial #1; //Method java/lang/Object."<init>":()V
    4: aload_0
    5:  iconst_0
    6: putfield  #2; //Field i:I
    9:  return

public void doitPost();
  Code:
    0: aload_0
    1:  dup
    2: getfield  #2; //Field i:I
    5:  dup_x1
    6: iconst_1
    7:  iadd
    8: putfield  #2; //Field i:I
    11: istore_1
    12:  return

public void doitPre();
  Code:
    0:  aload_0
    1: dup
    2:  getfield  #2; //Field i:I
    5: iconst_1
    6:  iadd
    7: dup_x1
    8:  putfield  #2; //Field i:I
    11:  istore_1
    12: return

}

The problem above is that these operators are implemented in byte code as read-modify-write. This creates a problem when multiple threads are operating on the same piece of state. Consider the following:

  1. Thread 1: getfield #2; // Thread 1 gets the value ‘0’
  2. Thread 2: getfield #2; // Thread 2 also gets the value ‘0’
  3. Thread 1: dup_x1, iconst_1, iadd, putfield #2 // Add ‘1’ and write it back
  4. Thread 2: dup_x1, iconst_1, iadd, putfield #2 // Add ‘1’ and write it back

At this point, the shared variable will have a value of ‘1’, despite having two auto-increment operations called on it! This can manifest itself in any number of nefarious ways. To better illustrate that this will happen if not guarded against, I’ve written a test and posted it on github. You can grab the test code here

Once you have the code, build and run it (assuming you have gradle) with:

$ git clone git://github.com/influenza/blog-snippets.git
$ cd blog-snippets/AutoincrementAtomicity/
$ gradle jar
$ java -jar build/libs/AutoincrementAtomicity-1.0.jar 4 1000

You’ll find that it breaks pretty quickly. Feel free to vary the number of threads and the number of iterations. Even for values as low as 2 threads and 20 iterations, this will break pretty quickly.

This is one of those places where the syntax obscures a complex operation - something that occurs frequently in java (think auto boxing). If you’re not sure how something behaves, it’s worth busting out javap on the generated class file to find out exactly what’s happening.

Until next time…