Illustrative variability: is this code safe?

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

But I do not use volatile . The idea of โ€‹โ€‹the following code is to cause an infinite loop if volatile omitted and be perfectly thread safe if volatile present. Is the following code thread safe? Do you have any other realistic and short example code that uses volatile , and without it a clearly incorrect result will be displayed?

Here is the code:

 public class VolatileTest implements Runnable { private int count; private volatile boolean stopped; @Override public void run() { while (!stopped) { count++; } System.out.println("Count 1 = " + count); } public void stopCounting() { stopped = true; } public int getCount() { if (!stopped) { throw new IllegalStateException("not stopped yet."); } return count; } public static void main(String[] args) throws InterruptedException { VolatileTest vt = new VolatileTest(); Thread t = new Thread(vt); t.start(); Thread.sleep(1000L); vt.stopCounting(); System.out.println("Count 2 = " + vt.getCount()); } } 
+3
source share
6 answers

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

Here is 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 the thread notes that stopped==true , it ensures that the work is completed and the result will be visible.

There is a relationship that comes from before volatile write to volatile read (from the same variable), so if there are two threads

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

Action A occurs before action B; entries in visible B.

+10
source

It has always been difficult for me to illustrate concurrency problems in a convincing way: well, well, this is all well and good going on, first and foremost, but why does one care? Is there a real problem? There are many, many poorly written, poorly synchronized programs โ€” and they still work most of the time.

I used to find a resort in โ€œwork most of the time when VS worksโ€ rhetoric, but to be honest, this is a weak approach. So I needed an example that would make the difference obvious - and preferably painful.

So here is a version that actually shows 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)); } } 

Simple show:

 Waiting 1319229217263 stopping 1319229227263 Waiting 1319229242728 stopped 1319229242728 Delay: 15 

That is, it takes more than ten seconds for the current thread (15 here) to notice that any changes have occurred.

With volatile you have:

 Waiting 1319229288280 stopping 1319229298281 stopped 1319229298281 Delay: 0 

that is, to leave immediately (almost). The resolution of currentTimeMillis is about 10 ms, so the difference is more than 1000 times.

Note that this was a version of Apple (ex-) Sun JDK, with the -server option. A 10 second wait has been added to let the JIT compiler know that the loop is hot enough and optimize it.

Hope this helps.

+1
source

A simplification of the @Elf example is further where another thread will never get a value that has been updated by another thread. Removing System.out.println, since the synchronized code inside println and out is static, somehow helps the other thread get the last value of the 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(); } } 
+1
source

UPDATE . My answer is incorrect, see the answer from the invincible.


It is not thread safe, since access to count does not exist, only one write stream. If there is another message flow, the count value will not be compatible with the number of updates.

The visibility of the count value for the main thread is ensured by checking the stopped mutable inside the getCount method. This is what is called piggybacking on synchronization in the Concurrency in practice book.

0
source

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

0
source

Incorrect code with which we cannot read 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; } } 

An example of using the 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
source

All Articles