Never synchronise on Java boxed primitives

In this article I will explain another dangerous pitfall related to the interning mechanism in Java. I explained what interning is and some of the dangers associated with it in my previous two posts about strings and boxed primitives, if you want to know more please check them out.
The pitfall we are going to analyse in this article involves Java immutability, boxed primitives and threads. A mix that can sometimes be quite explosive.

Immutability in Java

Many Java classes like Integer and Long are immutable. A class is immutable if we cannot change its value after we have instantiated it.

Integer value = 10;

The Integer class is immutable, its value cannot be changed.
So what happens when we do this?

value++;


We just incremented the value by one, so it has changed, right?
No, when this java code becomes byte code, the statement above is pretty much the same as this:

value = new Integer(value + 1);

Since Integer is immutable, as soon as we change its value a new Integer instance with the new value is created and assigned to value. This is immutability and it works for String, Boolean, Long, Double, BigInteger and many other classes.

Boxed primitives

Boxed primitives can be really useful, and they can be very tricky. We use them to represent numeric values in an object oriented way. They are called boxed primitives, or wrapped primitives, because they wrap a primitive Java type such as int and long inside a class.

Threads

In a multi-thread system we often need to synchronize portions of the code to make sure that no thread except one operates in a critical section, or on some object.

Where it all goes wrong

Consider the following code where we have an Integer value that can be incremented by one thread and decremented by another thread


private Integer value = 0;
...
// Thread #1
synchronized(value) {
  ...				
  value++;
  ...
}
...
...
// Thread #2
synchronized(value) {
  ...				
  value--;
  ...
}

The synchronized block works by acquiring the lock of the value object, but value is an immutable auto boxed primitive, therefore the instance it points to will no longer be the locked instance after the increment.
The following code is equivalent to the snippet above, however it immediately looks a lot more scary

private Integer value = 0;
...
// Thread #1
synchronized(value) {
  ...				
  value = new Integer(value+1);
  ...
}
...
...
// Thread #2
synchronized(value) {
  ...				
  value = new Integer(value-1;
  ...
}

Conclusion

I hope this article was clear enough, and thank you for reading it. Please let me know in the comments what you think about this. I do not have much to add at this point except, be careful what you synchronise on.

Unique opportunity! Help a fellow grow his blog!

Hi there! If you’ve read this far maybe you think this was useful, or fun, or I don’t know what but for some reason You Got Here! Great! Please consider sharing this post with your network, I am trying to get The Code Butchery to grow so I can provide more content like this, will you help me in my journey? Thank you!

Share this

Comments

  1. Hi FRANCESCO RIGONI,
    Thanks for the explanation, it is really clear.
    I was taking some tests here and I faced I different situation but, in the same context and I couldn’t understand that.

    Look this:

    
    private long startTime = System.currentTimeMillis();
    private Integer valueA = 0;
    private Integer valueB = 0;
    ...
    //thread 1
    synchronized(valueA) {
      ...				
      Thread.sleep(1000);
      valueA++;
      System.out.println(System.currentTimeMillis() - startTime);
      ...
    }
    //thread 2
    synchronized(valueB) {
      ...				
      Thread.sleep(1000);
      valueB++
      System.out.println(System.currentTimeMillis() - startTime);
      ...
    }
    

    The last printed value will always be more than 2 seconds.

    What happens here is that they are sharing the same locker, it is like they are using a kind of *Integer Autoboxing* as locker. If I change one of the “value” to use a Long, it will work.

    1. Hi Willian, thanks for your comment. That is interesting, I think what is happening here is that the JVM interns the 0 value and so every time you use it you get the same instance. That happens because it is used as a literal. I believe your issue can be fixed also by doing Integer valueA = new Integer(0);. I wrote another post about this: https://thecodebutchery.com/2013/02/25/java-long-and-integer-objects-interning-and-comparison-methods/
      Can you try that and let me know?

      1. Hi Francesco, thanks for your reply.
        Your idea fix it.

        After that, I decided to have one more try on the problem and I figure out what was happening.
        My lockers were using the same immutable variable “Integer a = -1” and “Integer b = -1”. If I change one of the variables to start with -2, it will works.

        I am not sure but, if that will happen between two different classes, the problem to synchronize boxed primitives it will be even worse. Can you imagine a API with this lock and your system using this API with the same lock?

        Thanks for your attention!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.