Java memory model and thread 04: Special rules for volatile variables __java memory model and thread

Source: Internet
Author: User
Tags goto visibility volatile

one, written in front of the words

Keyword volatile can be said to be the Java Virtual Machine provides the most lightweight synchronization mechanism, but it is not easy to be completely correct and complete understanding, so many programmers are accustomed to not to use it, encountered in the need to deal with multi-threaded data competition problems when the general use of synchronized to synchronize. Understanding the semantics of volatile variables is useful for understanding other features of multithreaded operations later.

second, the key word volatile

When a variable is defined as volatile, it has two properties.

The first is to guarantee the "visibility" of this variable for all threads, where "visibility" means that when a thread modifies the value of the variable, the new value is immediately known to other threads. And ordinary variables do not do this, the value of ordinary variables passed between threads need to be done through the main memory, for example, thread a modifies the value of a generic variable and then writes back to the main memory, while the other thread B is read from the main memory after line a writeback completes, and the new variable value is not visible to the B thread.


As for the visibility of volatile variables, often misunderstood by developers, see the following common description: "The volatile variable is immediately visible to all threads, and all read and write operations to the volatile variable are immediately reflected in other threads, in other words, Volatile variables are consistent across threads, so operations based on volatile variables are safe under concurrency. There is nothing wrong with the arguments in this sentence, but the argument does not come to the conclusion that "operations based on volatile variables are safe under concurrency". The volatile variable does not have a consistency problem in the working memory of each thread (in the working memory of each thread, the volatile variable can also be inconsistent, but because it is refreshed before use, the execution engine does not see an inconsistency, so it can be considered that there is no consistency problem), But the operation in Java is not atomic operation, cause the operation of the volatile variable is as unsafe as the concurrent, we can use a simple code to explain the reason, please see the example below.

Package com.js.article04;
/**
 * Volatile variable self-augmentation test
 * @author Jiangshuai * * */Public
class Volatiletest {public
	static volatile int race = 0;
	public static void Increase () {
		race++;
	}
	public static final int thread_count =;
	public static void Main (string[] args) {
		thread[] threads = new Thread[thread_count];
		for (int i=0;i<thread_count;i++) {
			threads[i] = new THREAD (The new Runnable () {
				
				@Override public
				void Run ( {for
					(int i = 0; i<10000;i++) {
						increase ();}}}
			);
			Threads[i].start ();
		}		waits for all cumulative threads to end while
		(Thread.activecount () >1)
			Thread.yield ();
		System.out.println (race);
	}


This code initiated 20 threads, each thread to the race variable 10,000 times, if the code can be correctly concurrency, the final output should be 200000. But when you run the code, you don't get the results you want, and you find that every time you run the program, the output is different, and it's a number less than 200000.


The problem is in the "race++" of the self-increment operation, which we use to javap the code to get the code listing as follows:

Compiled from ' Volatiletest.java ' public class Volatiletest {public static volatile int race;

  public static final int thread_count;
    public Volatiletest (); code:0: Aload_0 1:invokespecial #1//Method Java/lang/object. "
    <init> ": () V 4:return public static void increase ();
       code:0: getstatic #2//Field race:i
       3:iconst_1 4:iadd 5:putstatic #2//Field race:i
    8:return public static void Main (java.lang.string[]);
       code:0: Bipush 2:anewarray #4//class Java/lang/thread 5:astore_1 6:iconst_0 7:istore_2 8:iload_2 9:bipush 11:if_icmpge           Oad_1 15:iload_2 16:new #4//class Java/lang/thread 19:dup 20:new #5//Class volatiletest$1 23:dup
      24:invokespecial #6//Method volatiletest$1. " <init> ":() V 27:invokespecial #7//Method Java/lang/thread." <init> ": (ljava/lang/runnable;) V 30:aastore 31:aload_1 32:iload_2 33:aaload 34:in          Vokevirtual #8//Method Java/lang/thread.start: () V 37:iinc 2, 1 40:goto 8 43:invokestatic #9//Method Java/lang/thread.activecou NT: () I 46:iconst_1 47:if _icmple 50:invokestatic #10//Method Java/lang/thread.yield: () V 53:goto 4
      3 56:getstatic #11//Field Java/lang/system.out:ljava/io/printstream;
      59:getstatic #2//Field race:i
    62:invokevirtual #12//Method Java/io/printstream.printl N: (I) V 65:return static {};                 code:0: Iconst_0 1:putstatic #2 Field race:i





 4:return}

The increase () method found to have only one line of code is made up of 4 byte code instructions in the class file (the return instruction is not generated by race++ and this instruction can not be computed). It is easy to analyze the reason for concurrency failure from the bytecode level: when the getstatic command race the value to the top of the stack, the volatile keyword guarantees that the race is correct at this time, but when executing iconst_1 and iadd these instructions, Other threads may have increased the value of the race, while the value at the top of the Operation Stack becomes out-of-date, so the putstatic command may synchronize the smaller race values into the main memory after execution.


Objectively speaking, it is still not rigorous to use bytecode to analyze concurrency problems, because even if only one byte-code instruction is compiled, it does not mean that executing this instruction is an atomic operation. A byte-code instruction in interpreting execution, the interpreter is going to run a lot of code to implement its semantics, and if it is compiled, a byte-code instruction may be translated into several local machine code directives, where the-xx:+printassembly parameter output disassembly is more rigorous. However, given that bytecode is already a problem, byte code is used here to parse it.


Since volatile variables can only guarantee visibility, we still need to ensure atomicity through lock (using atomic classes in synchronized or java.util.concurrent) in scenarios that do not conform to the following two rules.

1. The result of the operation does not depend on the current value of the variable, or can ensure that only a single thread modifies the value of the variable.

2, the variable does not need to join with other state variables to participate in invariant constraints.

This scenario is ideal for using volatile variables to control concurrency when the shutdown () method is invoked to ensure that the DoWork () method that executes in all threads stops immediately.

Package com.js.article04;

public class VolatileTest2 {
	volatile boolean shutdownrequested;
	public void shutdown () {
		shutdownrequested = true;
	}
	public void DoWork () {while
		(!shutdownrequested) {
			//do stuff
		}
	}
}


The second semantics of using the volatile variable is to prohibit instruction reordering optimizations, and ordinary variables only guarantee that the correct results are obtained in all the places that rely on the assignment results during the execution of the method, and that the order of the variable assignment is consistent with the order of execution in the program code. Because this is not perceptible during the execution of a thread's method, this is what is described in the Java memory model as "line Agenda semantics" (Within-thread as-if-serial semantics).



Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.