20

Is there any performance difference between AtomicInteger and Integer?

0

6 Answers 6

28

The choice of these two types should not depend on the performance. The main choice for AtomicInteger is if you want to achieve thread safety with the operations on the integer.

However the performace difference might strongly depend on the choosen operating system, as the detailed implementation of atomic operations depend on the operating system.

Sign up to request clarification or add additional context in comments.

6 Comments

+1. AtomicInteger also provide a cheap mutable integer, even in a non-concurrent use-case.
-1: AtomicInteger exists mainly for performance reason (to implement lock-free data structures). If thead safety were the only concern, you could simply use synchronization.
@MichaelBorgwardt: just because thread-safety is possible with just synchronization doesn't mean that AtomicInteger doesn't provide thread-safe integer operations. The point of the answer is that the raw performance of Integer and AtomicInteger is not what guides the choice of one or the other. What guides the choice is the intended usage: do we need a mutable thread-safe integer, or do we simply want to rap an integer into an immutable object?
@JB Nizet: mutability is not mentioned in the answer. And apart from that, how can performance not be what guides the choice when one of the choices exists because of performance?
@MichaelBorgwardt: The question of the OP doesn't even mention multi-threading. He just asks if there is a performance difference between Integer and AtomicInteger. AtomicInteger has not been created to have a faster Integer. It has been created to provide a thread-safe, lock-free, mutable Integer. If you don't need mutability and are not executing in a multi-threaded environment, the performance of both doesn't matter. You choose the most appropriate type: Integer. AtomicInteger should only be considered if you need mutability (and thread-safety).
|
22

AtomicInteger allows some (not all!) operations that would otherwise require synchronization to be performed in a lock-free manner using special hardware instructions. How this affects performance is somewhat complex:

  • First, it's a micro-optimization that will only matter if this particular operation is on your application's critical path.
  • The special hardware instructions may not be available on non-mainstream platforms, in which case AtomicInteger will probably be implemented using synchronization.
  • The JVM can often optimize away locking overhead when there is no contention (e.g., a single threaded application). In that case, there's probably no difference.
  • If there is low to moderate lock contention (i.e. multiple threads, but they mostly do other things than just accessing that integer), the lock-free algorithm performs better than synchronization.
  • If there is very heavy lock contention (i.e. lots of threads that spend a lot of time trying to access that integer), synchronization may perform better because the lock-free algorithm is based on constantly retrying the operation when it fails due to a collision.

1 Comment

Exactly the types of facts I've been looking for. Thanks.
12

Well, if you use it in multithreaded environment, as a, e.g. counter, then you have to synchronize access to the Integer

public final class Counter { private long value = 0; public synchronized long getValue() { return value; } public synchronized long increment() { return ++value; } } 

While you can have much better performance with AtomicInteger without synchronization

public class NonblockingCounter { private AtomicInteger value; public int getValue() { return value.get(); } public int increment() { return value.incrementAndGet(); } } 

Recommended reading http://cephas.net/blog/2006/09/06/atomicinteger/

EDIT use incrementAndGet

5 Comments

Why not using incrementAndGet?
you mean getAndIncrement? Well, good question sir, I guess I was too quick and lazy.
No, I mean incrementAndGet. getAndIncrement would be equivalent to return i++. But your first snippet returnn ++i;
Umm... yupp, maybe I should focus only on my work now, thanks.
Also, Brian Goetz on IBMWorks (new link) ibm.com/developerworks/library/j-jtp11234/index.html
7

Came across this posting today but wanted to share my results (Please no comments on code as I had to hand type the following classes as the system I ran this one on was not connected to the internet :)

Bottom line the output from the code below was as follows:

ATOMIC Results: Elapsed = 25257 ms, ExpectedValue = 50000, FinalValue = 50000, true PrImItIvE Results: Elapsed = 25257 ms, ExpectedValue = 50000, FinalValue = 48991, false

For my usage in my particular app I have chosen to use Atomic values for status numbers in a monitoring class. In case someone else wanted to view some hard results I opted to post this information.

Have a great day!

Classes:

I created a main class with a primitive long and an atomic long and accessor increment methods, an IncrementAtomicRunnable and an IncrementPrimitiveRunnable.

LongOverhead:

public class LongOverhead{ AtomicLong atomicLong; long primitiveLong; public LongOverhead(){ atomicLong = new AtomicLong(0l); primitiveLong = 0l; } public void incrAtomicLong(){ atomicLong.getAndAdd(1l); } public long getAtomicLong(){ return atomicLong.get(); } public void incrPrimitiveLong(){ primitiveLong++; } public long getPrimitiveLong(){ return primitiveLong; } public static void main(String [] args){ String template = "%s Results: Elapsed = %d ms, ExpectedValue = %d, FinalValue = %d, %b"; int loopTotal = 1000; int waitMilliseconds = 25; int totalThreads = 50; int expectedValue = loopTotal * totalThreads; int whileSleep = 250; LongOverhead atomic = new LongOverhead(); LongOverhead primitive = new LongOverhead(); List<Thread> atomicThreads = new ArrayList<>(); List<Thread> primitiveThreads = new ArrayList<>(); for(int x=0;x<totalThreads;x++){ Thread a = new Thread(new IncrementalAtomicRunnable(atomic, loopTotal, waitMilliseconds), "AtomicIncr" + x); atomicThreads.add(a); Thread p = new Thread(new IncrementalPrimitiveRunnable(primitive, loopTotal, waitMilliseconds), "PrimitiveIncr" + x); primitiveThreads.add(p); } boolean cont = true; long atomicStart = System.currentTimeMillis(); for(Thread t: atomicThreads){ t.start(); } while(cont){ try{ Thread.sleep(whileSleep); }catch(InterruptedException e){ e.printStackTrace(); } boolean foundAlive = false; for(Thread t: atomicThreads){ foundAlive = (State.TERMINATED != t.getState()); if(foundAlive){ break; } } cont = foundAlive; } long atomicFinish = System.currentTimeMillis(); long atomicElapsed = atomicFinish - atomicStart; long atomicFinal = atomic.getAtomicLong(); cont = true; long primitiveStart = System.currentTimeMillis(); for(Thread t: primitiveThreads){ t.start(); } while(cont){ try{ Thread.sleep(whileSleep); }catch(InterruptedException e){ e.printStackTrace(); } boolean foundAlive = false; for(Thread t: primitiveThreads){ foundAlive = (State.TERMINATED != t.getState()); if(foundAlive){ break; } } cont = foundAlive; long primitiveFinish = System.currentTimeMillis(); long primitiveElapsed = primitiveFinish - primitiveStart; long primitiveFinal = primitive.getPrimitiveLong(); System.out.println(String.format(template, "ATOMIC", atomicElapsed, expectedValue, atomicFinal, (expectedValue==atomicFinal))); System.out.println(String.format(template, "PrImItIvE", primitiveElapsed, expectedValue, primitiveFinal, (expectedValue==primitiveFinal))); } 

IncrementAtomicRunnable:

public class IncrementAtomicRunnable implements Runnable{ protected LongOverhead oh; protected int loopTotal; protected int waitMilliseconds; protected String currentThreadName; public IncrementAtomicRunnable(LongOverhead oh, int loopTotal, int waitMilliseconds){ this.oh = oh; this.loopTotal = loopTotal; this.waitMilliseconds = waitMilliseconds; } @Override public void run(){ currentThreadName = Thread.currentThread().getName(); System.out.println(currentThreadName + " for ATOMIC is starting....."); for(int x=0;x<loopTotal;x++){ oh.incrAtomicLong(); try{ Thread.sleep(waitMilliseconds); }catch(InterruptedException e){ System.out.println("InterruptedThread[" + currentThreadName + "], eating exception @@@@@"); } } System.out.println("....." + currentThreadName + " for ATOMIC is finished."); } } 

and finally IncrementPrimitiveRunnable:

public class IncrementPrimitiveRunnable extends IncrementAtomicRunnable{ public IncrmentPrimitiveRunnable(LongOverhead oh, int loopTotal, int waitMilliseconds){ super(oh, loopTotal, waitMilliseconds); } @Override public void run(){ super.currentThreadName = Thread.currentThread().getName(); System.out.println(currentThreadName + " for PRIMITIVE is starting....."); for(int x=0;x<loopTotal;x++){ oh.incrPrimitiveLong(); try{ Thread.sleep(waitMilliseconds); }catch(InterruptedException e){ System.out.println("InterruptedThread[" + currentThreadName + "], eating exception @@@@@"); } } System.out.println("....." + currentThreadName + " for PRIMITIVE is finished."); } } 

Comments

1

Other than the very minor synchronization overhead, no.

2 Comments

There is no synchronization involved
It uses CAS instructions. You will not find the word "synchronized" anywhere in the class. The whole point of the atomic classes is to avoid synchronization.
0

The short answer is yes: Integer is slower because it is immutable.

When you use the ++ operator on an Integer object, a new Integer object with the incremented value is actually created, through auto-unboxing and auto-boxing.

Here is an example code to give an idea of the performance difference, adding also int as reference:

public class AtomicIntegerTest { public static void main(String[] args) { int n = 1000000000; int i = 0; long start = System.currentTimeMillis(); while (i < n) { i++; } long end = System.currentTimeMillis(); System.out.println("int: " + (end - start) + "ms"); Integer in = 0; start = System.currentTimeMillis(); while (in < n) { in++; } end = System.currentTimeMillis(); System.out.println("Integer: " + (end - start) + "ms"); AtomicInteger ai = new AtomicInteger(0); start = System.currentTimeMillis(); while (ai.get() < n) { ai.incrementAndGet(); } end = System.currentTimeMillis(); System.out.println("AtomicInteger: " + (end - start) + "ms"); } } 

And here is the result executed on a Linux box with an AMD Ryzen 7 6800U CPU:

$ javac AtomicIntegerTest.java $ java AtomicIntegerTest int: 1ms Integer: 2278ms AtomicInteger: 1773ms 

3 Comments

please check How do I write a correct micro-benchmark in Java? I got different results with your code: screenshot
@user85421 that's a very interesting read, thanks for sharing! What do you think I did wrong? What operating system, CPU and command line arguments did you use?
Windows, Laptop, Intel i7 12800/2400MHz/14 core, Eclipse, no arguments ... but similar can be seen on ideone.com/m8rhHp but with less repetitions, otherwise we get "Time limit exceeded" (when doing the last test AtomicInteger)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.