Java Theory and Practice: Correct Use of volatile Variables

Source: Internet
Author: User

Volatile variable User Guide

Level: Intermediate

Brian Goetz
Brian.goetz@sun.com
), Senior Engineer, Sun Microsystems

July 05, 2007

The Java language includes two internal synchronization mechanisms: Synchronous block (or method) and volatile variables. Both mechanisms are proposed to achieve code thread security. Among them, the synchronization of volatile variables is poor (but sometimes it is simpler and has lower overhead), and its usage is more error-prone. In this issueJava Theory and Practice
Brian Goetz will introduce several modes for correctly using volatile variables, and give some suggestions on their applicability restrictions.


The volatile variable in Java can be seen as a "to a lesser extent"synchronized
"; Andsynchronized
Compared with the block, the volatile variable requires less encoding and less runtime overhead. However, the only function it can implement issynchronized
. This article introduces several effective modes for Using volatile variables, and emphasizes the situations where volatile variables are not suitable for use.

The lock provides two main features:Mutual Exclusion)
AndVisibility)
.
Mutex allows only one thread to hold a specific lock at a time. Therefore, you can use this feature to implement a coordinated access protocol for shared data. In this way, only one thread can use the shared data at a time. More visibility
Complicated. It must ensure that the changes made to the shared data before the lock is released are visible to another thread that subsequently acquires the lock --
Without the visibility guarantee provided by the synchronization mechanism, the shared variables seen by the thread may be the values before modification or inconsistent values, which will lead to many serious problems.

Volatile variable

The volatile variable hassynchronized
But not atomic. This means that the thread can automatically discover the latest value of the volatile variable. Volatile
Variables can be used to provide thread security, but can only be applied to a very limited set of Use Cases: There is no constraint between multiple variables or between the current value of a variable and the modified value of a variable. Therefore, use
Volatile is not enough to implement counters, mutex locks, or any class (such as "Start
<= End ").

For simplicity or scalability, you may prefer to use volatile variables instead of locks. When using
When the volatile variable is not locked, some usage (idiom) is easier to code and read. In addition, volatile
Variables do not cause thread blocking like locks, so they rarely cause scalability problems. In some cases, if the read operation is much larger than the write operation, volatile
Variables can also provide performance advantages over locks.

Conditions for correct use of volatile Variables

You can only replace the lock with the volatile variable in a limited number of cases. To enable the volatile variable to provide ideal thread security, the following conditions must be met simultaneously:

  • Write operations on variables do not depend on the current value.
  • This variable is not included in the variant with other variables.

In fact, these conditions indicate that the valid values that can be written into the volatile variable are independent of the State of any program, including the current state of the variable.

The limitation of the first condition makes the volatile variable not used as a thread security counter. Although incremental operations (x++
) It looks like a separate operation. In fact, it is a combination of read-Modify-write operation sequences and must be executed in an atomic manner, while volatile cannot provide the required atomic features. To achieve the correct operation, you needx
The value remains unchanged during the operation, while the volatile variable cannot. (However, if you adjust the value to write only from a single thread, you can ignore the first condition .)

Most programming scenarios conflict with one of these two conditions, making the volatile variable notsynchronized
This method is applicable to thread security. Listing 1 shows a non-thread-safe value range class. It contains a non-variant-the lower bound is always less than or equal to the upper bound.

List 1. Non-thread-safe value range class

@ Notthreadsafe <br/> public class numberrange {<br/> private int lower, upper; <br/> Public int getlower () {return lower ;} <br/> Public int getupper () {return upper;} <br/> Public void setlower (INT value) {<br/> If (value> upper) <br/> throw new illegalargumentexception (...); <br/> lower = value; <br/>}< br/> Public void setupper (INT value) {<br/> If (value <lower) <br/> throw new illegalargumentexception (...); <br/> upper = value; <br/>}< br/>

This method limits the range of state variables, solower
The definition of the upper field as the volatile type does not fully implement the thread security of the class; therefore, synchronization still needs to be used. Otherwise, if two threads use inconsistent values for execution at the same timesetLower
AndsetUpper
Otherwise, the range will be in an inconsistent state. For example, if the initial status is(0, 5)
, Thread a calls at the same timesetLower(4)
And thread B callssetUpper(3)
Obviously, the values of the two operations do not meet the conditions, so both threads will pass the check to protect the variant, so that the final range value is(4, 3)
-- An invalid value. For other operations on the scope, we needsetLower()
AndsetUpper()
Operation atomicity -- defining a field as the volatile type cannot achieve this goal.

Performance Considerations

The main reason for using the volatile variable is its simplicity: in some cases, using the volatile variable is much easier than using the corresponding lock. The secondary reason for using the volatile variable is its performance: in some cases, the volatile variable synchronization mechanism has better performance than the lock.

It is difficult to make accurate and comprehensive comments, such as "X is always faster than Y", especially for internal JVM operations. (For example, in some cases, the VM may be able to completely delete the lock mechanism, which makes it difficult for us to abstract and comparevolatile
Andsynchronized
.) That is to say, in most of the current processor architectures, the volatile read operation overhead is very low-almost the same as the non-volatile read operation. While
The overhead of volatile write operations is much higher than that of non-volatile write operations, because to ensure visibility, You need to implement memory definition (memory)
Fence), even so, the total cost of volatile is still lower than the lock acquisition.

Volatile operations do not cause congestion like locks. Therefore, volatile provides some scalable features that are better than locks when volatile can be safely used. If the number of read operations far exceeds the write operation, compared with the lock, the volatile variable can usually reduce the performance overhead of synchronization.

Correct volatile Mode

Very
Multi-concurrency experts often guide users away from volatile.
Variables, because using them is more error-prone than using locks. However, if you follow well-defined patterns with caution, you can safely use volatile in many scenarios.
Variable. Always remember the restrictions on Using volatile-volatile can be used only when the status is truly independent from other content in the program --
This rule can avoid extending these patterns to insecure use cases.

Mode #1: Status flag

The standard usage of volatile variables may only use a Boolean status flag to indicate an important one-time event, such as initialization or request downtime.

Many applications contain a control structure in the form of "execute some work when you are not ready to stop the program", as shown in Listing 2:


Listing 2. Using the volatile variable as a status flag

Volatile Boolean shutdownrequested; <br/>... <br/> Public void Shutdown () {shutdownrequested = true ;}< br/> Public void dowork () {<br/> while (! Shutdownrequested) {<br/> // do stuff <br/>}< br/>

It is likely to be called from outside the loop
shutdown()
Method -- that is, in another thread -- therefore, you need to execute some synchronization to ensure correct implementation.shutdownRequested
Variable visibility. (It may be called from the JMX listener, the Operation listener in the GUI event thread, through RMI, through a Web service, etc ). However
synchronized
The block compiling cycle is much more difficult than using the volatile status flag shown in Listing 2. Because
Volatile simplifies encoding and does not depend on any other State in the program. Therefore, volatile is ideal for use here.

One common feature of this type of State mark is: there is usually only one state transition;shutdownRequested
Flag fromfalse
Converttrue
And the program stops. This mode can be extended to the status flag of the back-and-forth conversion, but can be extended only when the conversion cycle is not noticed (fromfalse
Totrue
, And then convertfalse
). In addition, some atomic state conversion mechanisms, such as atomic variables, are required.

Mode #2: one-time Safe publication)

Lack of synchronization makes visibility impossible, making it more difficult to determine when to write object references instead of primitive values. In the absence of synchronization, you may encounter an update value referenced by an object (from another line
And the old value of the object status. (This is the root cause of the famous double-checked-locking problem.
When you perform a read operation during synchronization, the problem is that you may see an update reference, but you will still see an incomplete object through this reference ).

One technique for implementing secure publishing objects is to define object reference as volatile type. Listing 3 shows an example in which the background thread loads some data from the database during the startup phase. When other code can exploit the data, it will check whether the data has been published before use.


Listing 3. Using the volatile variable for one-time secure Publishing

Public class backgroundfloobleloader {<br/> Public volatile flooble theflooble; <br/> Public void initinbackground () {<br/> // do lots of stuff <br/> theflooble = new flooble (); // This is the only write to theflooble <br/>}< br/> public class someotherclass {<br/> Public void dowork () {<br/> while (true) {<br/> // do some stuff... <br/> // use the flooble, but only if it is ready <br/> If (floobleloader. theflooble! = NULL) <br/> dosomething (floobleloader. theflooble); <br/>}< br/>

IftheFlooble
Reference is not of the volatile type,doWork()
The code intheFlooble
Will get an incomplete constructedFlooble
.

A necessary condition for this mode is that the published object must be thread-safe, or a valid immutable object (valid immutable means that the object state will never be modified after it is released ). Volatile type references can ensure the visibility of objects in the form of release. However, if the object state changes after release, additional synchronization is required.

Mode #3: independent observation)

Another simple mode for Safely Using volatile is: Regular "release"
Observe the results for internal use of the program. For example, assume that an environmental sensor can feel the ambient temperature. A background thread may read the sensor every several seconds and update
Volatile variable. Other threads can then read this variable to view the latest temperature value at any time.

Another application that uses this mode is to collect program statistics. Listing 4 shows how the identity authentication mechanism remembers the name of the last user logged on. Will be used repeatedlylastUser
Reference to publish a value for other parts of the program.
Listing 4. Using the volatile variable to publish multiple independent observations

Public class usermanager {<br/> Public volatile string lastuser; <br/> Public Boolean authenticate (string user, string password) {<br/> Boolean valid = passwordisvalid (user, password); <br/> If (valid) {<br/> User u = new user (); <br/> activeusers. add (U); <br/> lastuser = user; <br/>}< br/> return valid; <br/>}< br/>

This mode is an extension of the previous mode. Publishing a value is used elsewhere in the program, but unlike publishing a one-time event, this is a series of independent events. This mode requires that the published value be valid and unchangeable-that is, the status of the value is not changed after the release. The code that uses this value must be clear that this value may change at any time.

Mode #4: "volatile Bean" Mode

The volatile bean mode applies to the framework that uses JavaBeans as the "honor structure. In volatile Bean
Mode, JavaBean is used as a group of containers with independent attributes of getter and/or setter methods. Volatile Bean
The basic principle of the pattern is: Many frameworks are the holders of variable data (for exampleHttpSession
But the objects in these containers must be thread-safe.

In the volatile bean mode, all data members of JavaBean are volatile type, and getter and setter
The method must be very common --
In addition to obtaining or setting the corresponding attributes, it cannot contain any logic. In addition, for data members referenced by an object, the referenced object must be valid and unchangeable. (This will disable attributes with array values, because when an array
The reference is declaredvolatile
Only references, rather than arrays, have volatile semantics ). For any volatile variable, the unchanged type or constraints cannot contain the JavaBean attribute. The example in listing 5 shows the JavaBean following the volatile bean mode:


Listing 5. person objects in volatile bean Mode

@ Threadsafe <br/> public class person {<br/> private volatile string firstname; <br/> private volatile string lastname; <br/> private volatile int age; <br/> Public String getfirstname () {return firstname ;}< br/> Public String getlastname () {return lastname ;}< br/> Public int getage () {return age ;}< br/> Public void setfirstname (string firstname) {<br/> This. firstname = firstname; <br/>}< br/> Public void setlastname (string lastname) {<br/> This. lastname = lastname; <br/>}< br/> Public void setage (INT age) {<br/> This. age = age; <br/>}< br/>

Volatile Advanced Mode

The models described in the previous sections cover most of the basic use cases. Using volatile in these models is very useful and simple. This section describes a more advanced mode in which volatile provides performance or scalability advantages.

Volatile
The advanced mode of the application is very fragile. Therefore, you must carefully prove the assumptions and these patterns are strictly encapsulated, because even small changes can damage your code! Similarly, use more advanced
Volatile
The reason for the use case is that it can improve performance and ensure that this performance benefit is truly determined before the advanced application mode starts. We need to weigh these models and discard readability or maintainability in exchange for possible features.
Benefits --
If you do not need to improve performance (or you cannot pass a strict test program to prove that you need it), this is probably a bad transaction, because you are likely to lose the candle, what you get is better than what you give up.
The value of things is lower.

Mode #5: read-write lock policies with low overhead

So far, you have learned that the volatile function is not enough to implement counters. Because++x
It is actually a simple combination of three operations (read, add, and storage). If multiple threads try to perform incremental operations on the Volatile counter at the same time, the update value may be lost.

However, if read operations far exceed write operations, you can use internal locks and volatile variables to reduce the overhead of Public Code paths. Use the thread-safe counter shown in Listing 6
synchronized
Make sure that the incremental operation is atomic and usevolatile
Ensure the visibility of the current result. If the update frequency is not frequent, this method can achieve better performance, because the overhead of the read path only involves volatile read operations, which is usually better than the overhead of a non-competitive lock acquisition.


Listing 6. Using volatile and synchronized together to implement "low-overhead read-write locks"

@ Threadsafe <br/> public class cheesycounter {<br/> // employs the cheap read-write lock trick <br/> // All mutative operations must be done with the 'This 'lock held <br/> @ guardedby ("this ") private volatile int value; <br/> Public int getvalue () {return value ;}< br/> Public synchronized int increment () {<br/> return value ++; <br/>}< br/>

This technology is called a "low-overhead read-write lock" because you use different synchronization mechanisms for read/write operations. Because the write operation in this example violates the use of volatile
So you cannot use volatile to safely implement the counter-you must use the lock. However, you can use volatile to ensure that the current valueVisibility
,
Therefore, you can use the lock to perform all changed operations and use volatile to perform read-only operations. The lock only allows one thread to access the value at a time, volatile
Multiple Threads are allowed to perform read operations. Therefore, when you use volatile to ensure the path of the read code, it is more shared than when you use the lock to execute all the code paths --
It is like a read-write operation. However, keep in mind the weakness of this pattern: if the most basic application of this pattern is exceeded, it will become very difficult to combine these two competing synchronization mechanisms.

Conclusion

Compared with the lock, the volatile variable is a very simple but fragile synchronization mechanism that will provide better performance and scalability than the lock in some cases. If you strictly follow the volatile usage conditions-that is, variables are truly independent from other variables and their previous values-in some cases, you can usevolatile
Replacesynchronized
To simplify the code. Howevervolatile
The code is often more error-prone than the code that uses the lock. The mode described in this article covers the availablevolatile
Replacesynchronized
The most common use cases. Following these patterns (note that they must not exceed their limits) helps you securely implement most use cases and use volatile variables for better performance.

References

Learning

  • For more information, see the original article on the developerworks global site.
    .

  • Java
    Concurrency in practice


    : How-to Manual for Developing concurrent programs using Java code, including creating and writing thread-safe classes and programs, avoiding performance impact, managing performance, and testing concurrent applications.
  • Popular Atoms
    : Introduced the new atomic variable class in Java 5.0. This feature extends the volatile variable to support atomic state conversion.
  • Introduction to non-Blocking Algorithms
    : Describes how to use atomic variables instead of locks to implement concurrent algorithms.
  • Volatiles
    : Get more information about the volatile variable from Wikipedia.
  • Java Technology Zone
    : Provides hundreds of articles on various aspects of Java programming.

About the author

Brian Goetz has been a professional software developer for 20 years. He is a senior engineer at Sun Microsystems and serves multiple JCP expert groups. Brian's work
Java concurrency in practice


Published by Addison-Wesley in May 2006. Please refer to the articles that Brian has published and will be published in mainstream publications in the industry
.

 

Source: http://www.ibm.com/developerworks/cn/java/j-jtp06197.html#2.0

 

Related Article

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.