Careful use of the volatile keyword in Java multithreaded programming

  • 2020-04-01 02:50:23
  • OfStack

The volatile keyword is well known to readers of Java multithreading. The volatile keyword is used to declare simple type variables such as int, float, Boolean, and so on. If these simple data types are declared volatile, their operation becomes atomic. But there are limits. For example, n in the following example is not atomic:


package mythread;
public class JoinThread extends Thread
{
public static volatile int n = 0;
public void run()
{
for (int i = 0; i < 10; i++)
try
{
n = n + 1;
sleep(3); //To make the results more random, delay by 3 milliseconds
}
catch (Exception e)
{
}
}
public static void main(String[] args) throws Exception
{
Thread threads[] = new Thread[100];
for (int i = 0; i < threads.length; i++)
//Create 100 threads
threads[i] = new JoinThread();
for (int i = 0; i < threads.length; i++)
//Run the 100 threads you just created
threads[i].start();
for (int i = 0; i < threads.length; i++)
//Continue after all 100 threads have executed
threads[i].join();
System.out.println("n=" + JoinThread.n);
}
}

If the operation on n is atomic level, the final output result should be n=1000. However, when the area code is executed, many times the output n is less than 1000, which means that n=n+1 is not an operation at atomic level. The reason is that the volatile keyword does not work if the current value of a simple variable declared as volatile is related to its previous value, which means that none of the following expressions are atomic operations:


n = n + 1;
n++;

If you want this situation to become an atomic operation, you need to use the synchronized keyword, and the above code can be changed to the following form:


package mythread;
public class JoinThread extends Thread
{
public static int n = 0;
public static synchronized void inc()
{
n++;
}
public void run()
{
for (int i = 0; i < 10; i++)
try
{
inc(); //N is equal to n plus 1, so it's going to be inc();
sleep(3); //To make the results more random, delay by 3 milliseconds
}
catch (Exception e)
{
}
}
public static void main(String[] args) throws Exception
{
Thread threads[] = new Thread[100];
for (int i = 0; i < threads.length; i++)
//Create 100 threads
threads[i] = new JoinThread();
for (int i = 0; i < threads.length; i++)
//Run the 100 threads you just created
threads[i].start();
for (int i = 0; i < threads.length; i++)
//Continue after all 100 threads have executed
threads[i].join();
System.out.println("n=" + JoinThread.n);
}
}

The code above changes n=n+1 to inc(), where the inc method USES the synchronized keyword for method synchronization. So be careful when using the volatile keyword, is not just a simple type variables using volatile, all of the variables are the original operation, when the value of a variable by itself on a decision, such as n = n + 1, n++, the volatile keyword will fail, only when the value of the variable has nothing to do with themselves on a value for the rest of the variable operation is atomic, n = m + 1, this is the original level. So be careful when using volatile keys. If you're not sure, use synchronized instead of volatile.


Related articles: