Java Theory and Practice: Correct Use of volatile Variables

Source: Internet
Author: User

From: http://www.ibm.com/developerworks/cn/java/j-jtp06197.html

Introduction:The Java language includes two internal synchronization mechanisms: Synchronous block (or method) and volatile variables. Both mechanisms are proposed to achieveCodeThread 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 PracticeBrian 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"; AndSynchronizedCompared 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. Visibility is more complex. It must ensure that changes made to the shared data before the lock is released are visible to another thread that subsequently acquires the lock-if this visibility guarantee is not provided by the synchronization mechanism, the shared variables seen by the thread may be values before modification or inconsistent values, which causes many serious problems.

Correct volatile Mode

Many concurrency experts actually tend to 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 the volatile variable in many scenarios. Always remember the restrictions on Using volatile-only when the status is truly independent fromProgramYou can use volatile only when other content is in. 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;... public void Shutdown () {shutdownrequested = true;} public void dowork () {While (! Shutdownrequested) {// do stuff }}

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

One common feature of this type of State mark is: there is usually only one state transition;ShutdownrequestedFlag fromFalseConvertTrueAnd 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 (fromFalseToTrue, 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, the updated value referenced by an object (written by another thread) may coexist with the old value in the object state. (This is the root cause of the famous double-checked-locking problem, where the object reference is read without synchronization, the problem is that you may see an updated 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 {public volatile flooble theflooble; Public void initinbackground () {// do lots of stuff theflooble = new flooble (); // This is the only write to theflooble} public class someotherclass {public void dowork () {While (true) {// do some stuff... // use the flooble, but only if it is ready if (floobleloader. theflooble! = NULL) dosomething (floobleloader. theflooble );}}}

IfThefloobleReference is not of the volatile type,Dowork ()The code inThefloobleWill 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 to regularly "publish" the observed 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 the volatile variable containing the current document. 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 repeatedlyLastuserReference to publish a value for other parts of the program.

Listing 4. Using the volatile variable to publish multiple independent observations

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

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 the volatile bean mode, JavaBean is used as a group of containers with independent attributes of getter and/or setter methods. The basic principle of the volatile bean mode is that many frameworks are the holders of variable data (for exampleHttpsessionBut the objects in these containers must be thread-safe.

In the volatile bean mode, all data members of the JavaBean are of the volatile type, and the getter and setter methods must be very common-apart from obtaining or setting corresponding attributes, they 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 the array reference is declaredVolatileOnly 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

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

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.

The advanced mode of volatile applications 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, the reason for using a more advanced volatile use case is that it can improve performance and ensure that this performance benefit is truly determined before the advanced mode is applied. You need to weigh these models and discard readability or maintainability in exchange for possible performance gains-if you do not need to improve performance (or you cannot prove that you need it through a strict test program ), this is probably a bad transaction, because you are likely to lose more than you lose, and the value you get is lower than the value you give up.

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++ XIt 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 6SynchronizedMake sure that the incremental operation is atomic and useVolatileEnsure 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"

@ Threadsafepublic class cheesycounter {// employs the cheap read-write lock trick // All mutative operations must be done with the 'This' lock held @ guardedby ("this") PrivateVolatileInt value; Public int getvalue () {return value;} publicSynchronizedInt increment () {return value ++ ;}}

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 first condition for Using volatile, you cannot use volatile to securely implement the counter-you must use the lock. However, you can use volatile to ensure that the current valueVisibilitySo you can use the lock to perform all the changed operations and use volatile for read-only operations. The lock only allows one thread to access the value at a time, and volatile allows multiple threads to perform read operations. Therefore, when volatile is used to ensure the read code path, it is more shared than using the lock to execute all code paths-just 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. In some cases, it will provide better performance and scalability than the lock. 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 use volatile instead of synchronized to simplify the code. However, the code that uses volatile is often more error-prone than the code that uses the lock. The model described in this article covers some of the most common use cases where you can use volatile instead of synchronized . 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.

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.