Search code examples
loopsif-statementconcurrencyatomic

Atomic Instructions: IFs and Loops


Are IF statements and loops such as while or do while atomic instructions in concurrent programming?

If not, is there a way to implement them atomically?

edit: Fixed some of my dodgy English.


Solution

  • In Java the only things which are atomic without any extra work are assignments. Anything else requires synchronisation, either via declaring method synchronized or using synchronized block. You can also use classes from java.concurrent - some of them use some more clever mechanisms to ensure synchronisation, rather than just declaring method synchronized what tends to be slow.


    Regarding if-statement and the question you asked in the comment about comparison n == m:

    Comparison is not atomic. First value of n has to be loaded (here value of m can still change), then value of m has to be loaded and then the actual comparison is evaluated (and at this point the actual values of both n and m can be already different than in the comparison).

    If you want it synchronised you would have to do something like this:

    public class Test {
    
        private static final Object lock = new Object();
    
        public static void main(String[] args) {
            if (equals(1, 2)) {
                // do something (not synchronised)
            }
        }
    
        public static boolean equals(int n, int m) {
            synchronized (lock) {
                return n == m;
            }
        }
    
    }
    

    However this raises a question why do you want to do this and what should be the lock (and what threads is the lock shared with)? I would like to see some more context about your problem, because currently I cannot see any reason of doing something like this.


    You should also remember that:

    • you cannot lock on primitives (you would have to declare both values as Integer)
    • locking on null will result in NullPointerException
    • lock is acquired on the value, not on the reference. Because integers in Java are immutable, assigning a new value to a field will result in creating a new lock, see the code below. Thread t1 acquires a lock on new Integer(1) while t2 acquires a lock on new Integer(2). So even though both threads lock on n, they can still be doing things in parallel.
    public class Test {
    
        private static Integer n = 1;
    
        public static void main(String[] args) throws Exception {
            Thread t1 = new Thread(() -> {
                synchronized (n) {
                    System.out.println("thread 1 started");
                    sleep(2000);
                    System.out.println("thread 1 finished");
                }
            });
            Thread t2 = new Thread(() -> {
                synchronized (n) {
                    System.out.println("thread 2 started");
                    sleep(2000);
                    System.out.println("thread 2 finished");
                }
            });
    
            t1.start();
            sleep(1000);
            n = 2;
            t2.start();
    
            t1.join();
            t2.join();
        }
    
        private static void sleep(int millis) {
            try {
                Thread.sleep(millis);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    
    }
    

    Have you considered using mutable AtomicInteger?