Java Multithreading: volatile keyword

Source: Internet
Author: User
Tags flushes volatile

I. Related concepts of memory model as we all know, when the computer executes the program, each instruction is executed in the CPU, while the execution of the instruction is bound to involve the reading and writing of the data. Since the temporary data in the program is stored in main memory (physical RAM), there is a problem, because the CPU is executing fast, and the process of reading data from memory and writing data to memory is much slower than the CPU executing instructions. Therefore, if the operation of the data at any time through the interaction with the memory, it will greatly reduce the speed of instruction execution.  So there's a cache in the CPU. That is, when the program is running, the data required for the operation is copied from the main memory to the CPU cache, then the CPU can be calculated directly from its cache to read data and write data to it, when the end of the operation, then the cache of data flushed to main memory. As a simple example, the following code:    i = i + 1;  reads the value of I from main memory when the thread executes the statement.  Then a copy is copied to the cache, then the CPU executes the instruction to add 1 to I, then writes the data to the cache, and finally flushes the most recent value of I in cache to main memory. For example, there are 2 threads executing this code, if the initial value of I is 0, then we want two threads to execute after the value of I becomes 2.  But will that be the case? There may be a situation where, initially, two threads read the value of I in the cache of their respective CPUs, then thread 1 adds 1, then writes the latest value of I to memory 1.  At this point in the cache of thread 2 The value of I is still 0, after adding 1 operations, I is the value of 1, and then thread 2 writes the value of I to memory. The value of the final result I is 1, not 2. This is a well-known cache consistency issue.  It is commonly said that the variable accessed by multiple threads is a shared variable.  That is, if a variable exists in multiple CPUs (typically in multithreaded programming), there may be a problem with cache inconsistencies.  In order to solve the problem of cache inconsistency, there are usually the following 2 solutions: 1) Through caching the consistency protocol by means of the bus plus lock# lock 2) There is a problem in the first way, because the other CPUs cannot access the memory during the locking of the bus, resulting in inefficiency. So there is a cache consistency protocol. The core idea is that when the CPU writes the data, if the variable that is found to be a shared variable, that is, a copy of the variable exists in other CPUs, it signals to the other CPU that the cache row of the variable is invalid, so when the other CPU needs to read the variable, it finds itself cachedThe cache row in which the variable is cached is not valid, it is re-read from memory.  volatile  variables in     Java  language can be seen as a kind of   "lesser  synchronized"; with   The synchronized  block requires less coding than the,volatile  variable, and is less expensive to run, but the functionality it can implement is only part of the  synchronized . This article introduces several modes of effective use of  volatile  variables, and emphasizes several cases where  volatile  variables are not suitable for use. The lock provides two main features: mutual exclusion (mutual exclusion)   and visibility (visibility). Mutual exclusion allows only one thread to hold a particular lock at a time, so you can use this attribute to implement a coordinated access protocol to shared data so that only one thread can use that shared data at a time. Visibility is more complex, and it must ensure that the changes made to the shared data before the lock is released are visible to the other thread that subsequently acquired the lock  --  if there is no such visibility guarantee provided by the synchronization mechanism, the shared variables that the thread sees may be pre-modified or inconsistent values. This will cause many serious problems.  Visibility      When a shared variable is modified by volatile, it guarantees that the modified value is immediately updated to main memory, and when another thread needs to read it, it will read the new value in RAM.  The common shared variable does not guarantee visibility, because when a common shared variable is modified, it is indeterminate when it is written to main memory, and when other threads go to read it may be the original old value at this time and therefore cannot guarantee visibility. In addition, visibility is ensured through synchronized and lock, and synchronized and lock ensure that only one thread acquires the lock at the same time and executes the synchronization code, and that changes to the variable are flushed to main memory before the lock is released. Visibility can therefore be guaranteed. The volatile variable modifier, if used appropriately, is less expensive to use and execute than synchronized because it does not cause thread context switching and scheduling.   Order      In the Java memory model, the compiler and processor are allowed to reorder instructions, and the reordering process does not affect the execution of a single-threaded procedure, but it can affect the correctness of multithreaded concurrency execution.  The memory semantics for volatile writes are as follows:
    • When a volatile variable is written, jmm flushes the shared variable in the local memory corresponding to the thread to main memory.
The memory semantics for volatile reads are as follows:
    • When a volatile variable is read, JMM will place the local memory corresponding to that thread as invalid. The thread next reads the shared variable from the main memory.

Conditions for correct use of volatile variables you can use volatile variables instead of locks in a limited number of situations. For the volatile variable to provide ideal thread safety, the following two conditions must be met:
    • The write operation on the variable does not depend on the current value.
    • The variable is not contained in an invariant that has other variables.
In fact, these conditions indicate that these valid values that can be written to a volatile variable are independent of the state of any program, including the current state of the variable. The first condition restricts the volatile variable from being used as a thread-safe counter. Although an incremental operation (x + +) looks like a separate operation, it is actually a combination of a read-modify-write sequence of operations that must be performed atomically, and volatile does not provide the necessary atomic properties. Implementing the correct operation requires that the value of X remain constant during operation, and the volatile variable cannot achieve this. (However, if you adjust the value to write only from a single thread, the first condition can be ignored.) Most programming scenarios conflict with one of these two conditions, making volatile variables not as universally applicable as synchronized for thread safety. Listing 1 shows a non-thread-safe numeric range class. It contains an invariant-the nether is always less than or equal to the upper bound. Listing 1. Non-thread-safe numeric range classes
@NotThreadSafe Public classNumberrange {Private intLower, Upper;  Public intGetlower () {returnLower;}  Public intGetupper () {returnUpper;}  Public voidSetlower (intvalue) {         if(Value >Upper)Throw Newillegalargumentexception (...); Lower=value; }     Public voidSetupper (intvalue) {         if(Value <Lower)Throw Newillegalargumentexception (...); Upper=value; }}
This approach restricts the state variables of the scope, so defining the  lower  and  upper  fields as  volatile  types does not fully implement the thread safety of the classes, and thus still requires the use of synchronization. Otherwise, if it happens that two threads perform  setLower  and  setUpper  at the same time using inconsistent values, the scope will be in an inconsistent state. For example, if the initial state is (0,&NBSP;5), at the same time, thread  A  call  setlower (4)   and thread  B  call  setupper (3), Obviously these two operations cross-deposit values are not eligible, then two threads will be used to protect the invariant check, so that the final range value is   (4, 3)  --  an invalid value. As for the range of other operations, we need to make  setlower ()   and Setupper ()   Operations atomically  --  and define the field as  volatile  Type is not possible for this purpose. The main reason for performance considerations with  volatile  variables is its simplicity: in some cases, using  volatile  variables is much simpler than using the corresponding locks. The secondary reason for using  volatile  variables is its performance: in some cases, the performance of the,volatile  variable synchronization mechanism is superior to the lock. It is difficult to make accurate and comprehensive evaluations, such as   "x  is always faster than  Y ", especially for  JVM  internal operations. (for example, in some cases  VM  may be able to completely remove the lock mechanism, which makes it difficult to compare the overhead of  volatile  and  synchronized  abstractly.) That is, the,volatile  read operation overhead on most processor architectures is very low  --  almost the same as non- volatile  read operations.  volatile  write operations are much more expensive than non- volatile  write operations, because it is necessary to ensure that visibility requires memory demarcation (memory fence), even ifThe total cost of volatile  is still lower than the lock fetch. volatile  operations do not clog like locks, so,volatile  can provide some of the more scalable features that are better than locks in cases where  volatile  is safe to use. If the number of read operations is much greater than the write operation,,volatile  variables compared to locks often reduce the performance overhead of synchronization. Correct use of  volatile  patterns many concurrency experts actually tend to steer users away from  volatile  variables, because using them is more error-prone than using locks. However, if you carefully follow some well-defined patterns, you can safely use  volatile  variables in many situations. Always keep in mind the limitations of using  volatile   --  can only be used when the state is truly independent of other content within the program  volatile --  This rule avoids extending these patterns to unsafe use cases. Patterns   #1: The status flags may implement the specification of  volatile  variables using only a Boolean status flag to indicate that an important one-time event has occurred, such as completion of initialization or request for downtime. Many applications contain a control structure in the form of   "Do some work when you are not ready to stop the program," as shown in Listing  2 : Listing 2. Using volatile variables as status flags
volatile Boolean shutdownrequested, .....  Public void true ; }  Public void doWork () {     while ( !  shutdownrequested) {         // do stuff    }}
It is possible to call  shutdown ()   method  --  in another thread from outside the loop  --  Therefore, you need to perform some kind of synchronization to ensure proper implementation   The visibility of the shutdownrequested  variable. (may be from the  JMX  listener, the action listener in the gui  event thread, through  RMI , via a  Web  service, etc.). However, writing loops using  synchronized blocks is a lot more cumbersome than using the  volatile  status flags shown in list  2 . Because  volatile  simplifies encoding and the status flag does not depend on any other state within the program, it is well suited to use  volatile. A common feature of this type of status token is that there is usually only one state transition;shutdownrequested  flag from  false  to  true, and then the program stops. This pattern can be extended to a state flag that is converted back and forth, but can be extended only if the conversion period is not detected (from  false  to  true, and then to  false). In addition, some atomic state transition mechanisms, such as atomic variables, are also required. Patterns   #2: A one-time security release (one-time safe publication) lack of synchronization results in the inability to achieve visibility, which makes it more difficult to determine when to write object references instead of primitive values. In the absence of synchronization, you may encounter an updated value for an object reference (written by another thread) and the old value of the state of the object exists at the same time. (This is the source of the famous double-check lock (double-checked-locking) problem, where the object reference is read without synchronization, and the problem is that you might see an updated reference, but still see the incompletely constructed object through the reference). One technique for implementing a secure publishing object is to define an object reference as a  volatile  type. The checklist  3  shows an example where a background thread loads some data from the database during the startup phase. Other code will check whether the data has been published before it is used when it can take advantage of this data.

Listing 3. Use volatile variables for a one-time security release

 Public classBackgroundfloobleloader { Public volatileflooble theflooble;  Public voidInitinbackground () {//Do lots of stuffTheflooble =NewFlooble ();//The only write to theflooble    }} Public classSomeOtherClass { Public voiddoWork () { while(true) {             //Do some stuff ...//Use the flooble, but only if it 's ready            if(Floobleloader.theflooble! =NULL) dosomething (floobleloader.theflooble); }    }}
If the theflooble reference is not a volatile type, the code in DoWork () will get a flooble that is not fully constructed when the reference to Theflooble is lifted. A prerequisite for this pattern is that the published object must be thread-safe or a valid immutable object (valid immutable means that the state of the object will never be modified after it is published). A reference to a volatile type ensures the visibility of the object's published form, but if the state of the object changes after it is published, additional synchronization is required. Mode #3: Independent observation (independent observation) Another simple mode for safe use of volatile is to periodically "publish" the results for internal use by the program. For example, suppose there is an ambient sensor that can feel the ambient temperature. A background thread might read the sensor every few seconds and update the volatile variable that contains the current document. The other thread can then read the variable, allowing it to see the latest temperature values at any time. Another application that uses this pattern is to collect statistics about the program. Listing 4 shows how the authentication mechanism remembers the name of the user who last logged on. Lastuser is used repeatedly to publish values for use by other parts of the program. listing 4. Using volatile variables for publication of multiple independent observation results
 Public classUsermanager { Public volatileString Lastuser;  Public BooleanAuthenticate (string user, string password) {Booleanvalid =passwordisvalid (user, password); if(valid) {User u=NewUser ();            Activeusers.add (U); Lastuser=user; }        returnvalid; }}
This pattern is an extension of the preceding pattern, and a value is published to be used elsewhere within the program, but unlike the release of a one-time event, this is a series of independent events. This mode requires that the value being published is valid immutable-that is, the status of the value will not change after it is published. The code that uses this value needs to be clear that the value may change at any time. Mode #4: The volatile bean mode volatile bean pattern is suitable for using JavaBeans as a framework for honor structures. In volatile bean mode, JavaBean is used as a container for a set of independent properties that have getter and/or setter methods. The basic principle of the volatile bean pattern is that many frameworks provide containers for variable data holders (for example, HttpSession), but the objects placed in those containers must be thread-safe. In volatile bean mode, all data members of the JavaBean are volatile types, and getter and setter methods must be very common-no logic can be included except to get or set the corresponding property. In addition, for data members referenced by an object, the referenced object must be valid and immutable. (This disables properties with array values, because when an array reference is declared as volatile, only the reference, not the array itself, has the volatile semantics). For any volatile variable, the invariant or constraint cannot contain the JavaBean property. The example in Listing 5 shows a JavaBean that adheres to the volatile bean pattern: Listing 5. The person object that adheres to the volatile bean pattern
@ThreadSafe Public classPerson {Private volatileString FirstName; Private volatileString LastName; Private volatile intAge ;  PublicString Getfirstname () {returnFirstName;}  PublicString Getlastname () {returnLastName;}  Public intGetage () {returnAge ;}  Public voidsetfirstname (String firstName) { This. FirstName =FirstName; }     Public voidsetlastname (String lastName) { This. LastName =LastName; }     Public voidSetage (intAge ) {          This. Age =Age ; }}
The pattern described in the previous sections of volatile advanced mode covers most of the basic use cases, and using volatile in these patterns is very useful and simple. This section describes a more advanced pattern in which volatile will provide performance or scalability benefits. The advanced mode of volatile applications is very fragile. Therefore, the hypothetical conditions must be carefully proven, and these patterns are tightly encapsulated, because even very small changes can damage your code! Similarly, the reason for using more advanced volatile use cases is that it improves performance and ensures that this performance benefit is really determined before you begin applying advanced mode. These patterns need to be weighed to give up readability or maintainability in exchange for possible performance gains--if you don't need to improve performance (or you can't prove you need it with a rigorous test program), then this is probably a bad deal because you're likely to lose the candle, It is worth less than what you give up. Mode #5: Low-cost read-write lock policy so far, you should know that volatile functionality is not enough to implement counters. Because ++x is actually a simple combination of three operations (read, add, store), it is possible that multiple threads might lose their updated values if they happen to attempt to perform incremental operations on the volatile counter at the same time. However, if the read operation is far more than the write operation, you can use the internal lock and the volatile variable to reduce the cost of the common code path. The thread-safe counters shown in Listing 6 use synchronized to ensure that the incremental operation is atomic and to use volatile to guarantee the visibility of the current result. This approach achieves better performance if the update is infrequent, because the cost of the read path involves only volatile read operations, which is usually better than the overhead of a non-competitive lock acquisition. Listing 6: Using volatile and synchronized to achieve "less expensive read-write locks"
@ThreadSafe Public classCheesycounter {//employs the cheap read-write lock trick//All mutative operations must is done with the ' this ' lock held@GuardedBy ("This")Private volatile intvalue;  Public intGetValue () {returnvalue;}  Public synchronized intincrement () {returnvalue++; }}
This technique is called a "low-cost read-write lock" because you use a different synchronization mechanism for read and write operations. Because the write operation in this example violates the first condition that uses volatile, you cannot use volatile to safely implement counters--you must use locks. However, you can use volatile in a read operation to ensure the visibility of the current value, so you can use the lock to make all the changes, using volatile for read-only operations. Where the lock allows only one thread to access the value at a time, volatile allows multiple threads to perform read operations, so when using the volatile guarantee to read the code path, it is much more shared-as in read-write operations-than using locks to execute all code paths. However, it is important to keep in mind the weaknesses of this model: if you go beyond the most basic application of the pattern, it will become very difficult to combine these two competing mechanisms. Concluding remarks compared to locks, Volatile variables are a very simple but very fragile synchronization mechanism, which in some cases provides better performance and scalability than locks. If you strictly follow the use of volatile conditions-that is, the variable is truly independent of the other variables and its own previous values-in some cases you can use volatile instead of synchronized to simplify the code. However, code that uses volatile is often more error-prone than code that uses locks. The pattern described in this article covers some of the most common use cases where volatile can be used instead of synchronized. Following these patterns (not exceeding their limits when using them) can help you implement most use cases safely and use volatile variables for better performance reference: Java Theory and Practice: Correct use of volatile variables

Java Multithreading: volatile keyword

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.