illustrating volatile : is this code thread-safe?

前端 未结 6 931
感动是毒
感动是毒 2020-12-17 05:49

I\'m trying to illustrate the use and importance of volatile with an example that would really not give a good result if volatile was omitted.

相关标签:
6条回答
  • 2020-12-17 06:32

    Simplifying @Elf example further, where the other thread will never get the value which was updated by other thread. Removing System.out.println as there is synchronized code inside println and out is static, somehow that helps the other thread to get the latest value of flag variable.

    public class VolatileExample implements Runnable {
       public static boolean flag = true; 
    
    
      public void run() {
         while (flag);
      }
    
      public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new VolatileExample());
        thread.start();
        Thread.sleep(1000L);
        flag = false;
        thread.join();
      }
    }
    
    0 讨论(0)
  • 2020-12-17 06:34

    UPDATE My answer is wrong, see answer from irreputable.


    It's not thread-safe, since access to count is not there's only one writer thread. Should there be another writer thread, value of count would be become inconsistent to the number of updates.

    Visibility of count value to main thread is ensured by checking stopped volatile inside getCount method. This is what is called piggybacking on synchronization in Concurrency in practice book.

    0 讨论(0)
  • 2020-12-17 06:35

    Wrong code with which we cannot assume x = 1 also if y is already 2:

    Class Reordering {
      int x = 0, y = 0;
      public void writer() {
        x = 1;
        y = 2;
      }
    
      public void reader() {
        int r1 = y;
        int r2 = x;
      }
    }
    

    Example of use of volatile keyword:

    class VolatileExample {
      int x = 0;
      volatile boolean v = false;
      public void writer() {
        x = 42;
        v = true;
      }
    
      public void reader() {
        if (v == true) {
          //uses x - guaranteed to see 42.
        }
      }
    }
    

    Source: http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html

    0 讨论(0)
  • 2020-12-17 06:44

    It was always hard to me to illustrate concurrency problems in a convincing way: well, fine, it's all nice about happens-before and stuff, but why should one care? Is there a real problem? There are lots and lots of poorly written, poorly synchronized programs—and they still work most of the time.

    I used to find a resort in a "works most of the time VS works" rhetoric—but, frankly, it's a weak approach. So what I needed is an example which would make difference obvious—and, preferably, painful.

    So here is a version which actually does show the difference:

    public class VolatileExample implements Runnable {
        public static boolean flag = true; // do not try this at home
    
        public void run() {
            long i = 0;
            while (flag) {
                if (i++ % 10000000000L == 0)
                    System.out.println("Waiting  " + System.currentTimeMillis());
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(new VolatileExample());
            thread.start();
            Thread.sleep(10000L);
            flag = false;
            long start = System.currentTimeMillis();
            System.out.println("stopping " + start);
            thread.join();
            long end = System.currentTimeMillis();
            System.out.println("stopped  " + end);
            System.out.println("Delay: " + ((end - start) / 1000L));
        }
    }
    

    A simple run shows:

    Waiting  1319229217263
    stopping 1319229227263
    Waiting  1319229242728
    stopped  1319229242728
    Delay: 15
    

    That is, it takes more than ten seconds (15 here) for a running thread to notice there was any change.

    With volatile, you have:

    Waiting  1319229288280
    stopping 1319229298281
    stopped  1319229298281
    Delay: 0
    

    that is, exiting (almost) immediately. The resolution of currentTimeMillis is around 10ms, so the difference is more that 1000 times.

    Note it was Apple's version of (ex-)Sun JDK, with -server option. The 10-second wait was added in order to let JIT compiler find out that the loop is hot enough, and optimize it.

    Hope that helps.

    0 讨论(0)
  • 2020-12-17 06:55

    Victor is right, there are issues with your code: atomicity and visibility.

    Here's my edition:

        private int count;
        private volatile boolean stop;
        private volatile boolean stopped;
    
        @Override
        public void run() {
            while (!stop) {
                count++; // the work
            }
            stopped = true;
            System.out.println("Count 1 = " + count);
        }
    
        public void stopCounting() {
            stop = true;
            while(!stopped)
               ; //busy wait; ok in this example
        }
    
        public int getCount() {
            if (!stopped) {
                throw new IllegalStateException("not stopped yet.");
            }
            return count;
        }
    
    }
    

    If a thread observes that stopped==true, it's guaranteed that the work completes and the result is visible.

    There is a happens-before relation from volatile write to volatile read (on the same variable), so if there are two threads

       thread 1              thread 2
    
       action A
           |
     volatile write  
                      \
                         volatile read
                              |  
                           action B
    

    action A happens-before action B; writes in A are visible by B.

    0 讨论(0)
  • 2020-12-17 06:55

    To illustrate the importance of the volatile keyword when it comes to concurrency, all you need to do is make sure that the volatile field is modified and read in a separate threads.

    0 讨论(0)
提交回复
热议问题