Search code examples
javamultithreadingincrementvolatile

How to solve multiple thread static variable incrementation?


So my problem essentially is,that even though I use static volatile int variable for incrementation some of my data doesn't remain unique which would be my goal(I number my elements).

public class Producer implements Runnable{
    private String str;
    private Fifo f;
    private int i;
    private static volatile int n=0;
    public Producer(String str,int i,Fifo f) ....
    
    public void run() {
        try {
            this.go();
        } catch (InterruptedException e) {
            ;
        }
    }
    
    void go() throws InterruptedException {
        while(true) {
                Thread.sleep(i);
                int l=n++;
                String k=str+" "+l+" ";
                f.put(k);
            System.out.println("produced "+str+" "+l+" "+System.currentTimeMillis()%100000);
        }
    }

}

My problem is in the function go(). I number my elements, I have multiple Producer objects running as independent threads, but sometimes they act like they have no clue whether n has been updated or not so I get same indexes. Any ideas? (I get what could be the problem, but I have no clue how to solve it.)


Solution

  • There seems to be a misunderstanding as to what volatile does. The keyword volatile introduces happens-before semantics between writes and reads. It does not, however, make multiple operations atomic.

    If we were to write the semantics of n++ "by hand" (please never do this, it is for explanatory purposes only), it would look something like that:

    final int result;
    n = (result = n) + 1;
    

    Ideone demo

    Looking at this code, we see that we have to:

    1. read the value of n,
    2. store it in some temporary variable result,
    3. increment it by 1, and
    4. write the (incremented) value back to n

    So we have multiple operations. If those operations are executed in parallel multiple times by different threads, then we can see a manifold of possible interweavings that lead to inconsistent data. For example, two threads could both read the (current) value of n. Both would increment the value by one and both would write the new value back to n. This means that two threads have executed the "increment", but the value of n has only incremented by 1 instead of 2.

    We can use specialized classes - in this case AtomicInteger - to avoid this problem. The usage looks something like this:

    public class Producer implements Runnable {
        ...
    
        private static final AtomicInteger n = new AtomicInteger(0);
    
        ...
    
        void go() throws InterruptedException {
            while(true) {
                    ...
                    int l = n.getAndIncrement();
                    ...
            }
        }
    
    }