One synchronized block versus multiple AtomicInteger increments

I understand that it is better to use AtomicInteger instead of synchronized to increase the overall int value . However, will it persist in case of multiple int values ?

Which of the following methods would be better and why? Is there a better way to do this for better performance?

1) Using a synchronized block:

int i, j, k, l;
public void synchronized incrementValues() {
    i++;j++;k++;l++;
}

      

2) Using AtomicInteger:

AtomicInteger i,j,k,l;
// Initialize i, j, k, l

public void incrementValues() {
    i.incrementAndGet();
    j.incrementAndGet();
    k.incrementAndGet();
    l.incrementAndGet();
}

      

Or will it be faster if I use ReentrantLock ?

3) Using ReentrantLock:

ReentrantLock lock = new ReentrantLock()
int i, j, k, l;
public void incrementValues() {
    lock.lock();
    try {
        i++;j++;k++;l++;
    } finally {
        lock.unlock();
    }
}

      

Here are my questions:

  • Is 3 the fastest of them all?
  • What about 2? For a single integer, 2 is faster than 1. Would 2 get slower than 1 if the number of integers increases?

Edit 1 Changed question Based on Matthias answer.

i, j, k, l are independent of each other. Individual increments must be atomic, not whole. It's okay if thread 2 changes i before thread 1 changes k.

Edit 2 More information based on comments so far

I am not looking for an exact answer, as I understand that it will depend on how these functions are used and the number of conflicts, etc. and measuring for each use case is the best way to determine the exact answer. However, I would like people to share their knowledge / articles, etc. that would shed light on the parameters / optimizations that affect the situation. Thanks for the post @ Marco13. It was informative.

+3


source to share


1 answer


First of all, # 2 is not thread safe. incrementAndGet () is atomic, however, calling the four incrementAndGet operations on a row is not. (for example, after the second incrementAndGet, another thread might hit the same method and start doing the same thing as in the example below.

T1: i.incrementAndGet();
T1: j.incrementAndGet();
T1: k.incrementAndGet();
T2: i.incrementAndGet();
T2: j.incrementAndGet();
T1: l.incrementAndGet();
T2: k.incrementAndGet();
T2: l.incrementAndGet();

      

then if it is between # 1 and # 3: if you are not using high speed stock trading, it does not matter to you. There might be really small differences (in the case of integers, probably in nanoseconds), but it doesn't really matter. However, I will always look for # 1 as it is much easier and also much safer to use (for example, imagine you forgot to put an unlock () in a finally block - then you might run into big problems)



Regarding your changes: For number 1: sometimes it can be important to atomize multiple values ​​at once. Note that the data is not only incremented but also read at the same time. You might assume that all variables have the same value at any given time. However, since the update operation is not atomic, when you read the data, it may be that i = j = k = 5 and l = 4 because the thread that incremented has not yet reached the last operation. Whether or not this is a problem depends a lot on your problem. If you don't need that kind of guarantee, don't worry.

For number 2: Optimization is tough and concurrency is even harder. I can only recommend NOT thinking about such micro-boundaries. These optimizations save nanoseconds at best, but make the code very complex. In the worst case, there is a false assumption or logical error in the optimization and you will run into concurrency issues. Chances are your optimization will be worse. Also note that the code you write will likely need to be maintained by someone else at a later point in time. And where you have saved milliseconds in your programming, you are running out of time to recycle a life trying to figure out what you want to do and why you are doing it this way while trying to fix this nasty multi-threading error. So for simplicity: Synchronized is the best thing to use.

The kissing principle DOES matter for concurrency.

+2


source







All Articles