An in-depth discussion of volatile variable _java in Java multi-threading

Source: Internet
Author: User
Tags visibility volatile

The volatile variable provides the visibility of the thread and does not guarantee thread security and atomicity.

What is the visibility of a thread:

Locks provide two main features: mutexes (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 at a time can use the shared data. Visibility is a little more complicated, it must ensure that the changes made to the shared data before the lock is released are visible to another thread that subsequently obtains the lock-if there is no such visibility guaranteed by the synchronization mechanism, the shared variable that the thread sees may be the value before the modification or inconsistent, which can cause many serious problems.

Look at the semantics of volatile :

Volatile is equivalent to the weak implementation of synchronized, that is to say, volatile implements the semantics of similar synchronized without locking mechanism. It ensures that updates to the volatile field are communicated to other threads in a predictable manner.

Volatile contains the following semantics:

(1) The Java storage model does not reorder the operations of the valatile instruction: this guarantees that the operation of the volatile variable is performed in the order in which the instructions appear.

(2) Volatile variables are not cached in registers (only owning threads are visible) or other places that are not visible to the CPU, and always read the results of volatile variables from main memory every time. This means that for volatile variables, other threads are always visible and are not using variables inside their own thread stacks. That is, in the Happens-before rule, after a write operation on a valatile variable, any subsequent read operation understands the result of the visible write.

Although the volatile variable is a good feature, volatile does not guarantee thread safety, that is, the operation of the volatile field is not atomic, and the volatile variable can only guarantee visibility (after one thread is modified that other threads can understand the result of seeing this change), To ensure atomicity, so far only lock!

The principle of using volatile:

Three principles for applying volatile variables:

(1) Write variable does not depend on the value of this variable, or only one thread modifies this variable

(2) The state of a variable does not need to participate in invariant constraints with other variables

(3) Access variables do not need to be locked

In fact, these conditions indicate that these valid values that can be written to the 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 read-modify-write sequences, which must be executed atomically, and volatile cannot provide the necessary atomic attributes. Implementing the correct operation requires that the value of X remain unchanged during the operation, while the volatile variable does not achieve this. (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 three conditions, making volatile variables not universally applicable to thread safety as synchronized. Listing 1 shows a value range class that is not thread safe. It contains an invariant-the lower bound is always less than or equal to the upper bound.

Correct use of volatile:

  Mode #1: Status flag

Perhaps the specification for implementing the volatile variable is simply using a Boolean status flag to indicate that an important one-off event occurred, such as completing initialization or requesting downtime.

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

Listing 2. Use the volatile variable as a status flag

Volatile Boolean shutdownrequested;

...  public void shutdown () {shutdownrequested = true;}

public void DoWork () {while

(!shutdownrequested) {

//do Stuff

}

}

It is possible to invoke the shutdown () method from outside the loop-that is, on another thread-so a synchronization is required to ensure that the visibility of the shutdownrequested variable is implemented correctly. (may be invoked from a JMX listener, an action listener in a GUI event thread, through RMI, through a Web service, and so on.) However, writing loops using the synchronized block is much more cumbersome 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 within the program, it is a good place to use volatile.

A common feature of this type of state tag is that there is usually only one state transition, the shutdownrequested flag is converted from false to true, and the program stops. This pattern can be extended to a status flag that is converted to and fro, but can be extended (from false to true and then to false) only if the conversion cycle is not detected. In addition, some atomic state conversion mechanisms, such as atomic variables, are also required.

  Mode #2: One-time security release (one-time safe publication)

Lack of synchronization results in the inability to implement visibility, which makes it more difficult to determine when to write an object reference instead of a literal value. In the absence of synchronization, you may encounter an update value that is referenced by an object (written by another thread) and the old value of that object's state. (This is the root cause of the double check Lock (double-checked-locking) problem that causes the name, where the object reference is read without synchronization, and the problem is that you may see an updated reference, but you will 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. Listing 3 shows an example of a background thread loading some data from the database during the startup phase. Other code, when it is able to take advantage of this data, checks whether the data has been published before use.

Listing 3. Use the volatile variable for one-time security release

public class Backgroundfloobleloader {public

volatile flooble theflooble;

public void Initinbackground () {

//do lots of stuff

theflooble = new flooble ();  Theflooble

}

} public

class SomeOtherClass {public

void DoWork () {while

(true) {

//  Do some stuff

... Use the flooble to but only if it is ready

if (floobleloader.theflooble!= null)

dosomething (Floobleloader.  theflooble);

}

}



If the theflooble reference is not a volatile type, the code in DoWork () will get an incomplete constructed flooble when the reference to Theflooble is lifted.

A prerequisite for this pattern is that the object being published must be thread-safe or a valid immutable object (effective immutable means that the state of the object will never be modified after it is published). A reference to the volatile type ensures the visibility of the publication of the object, but additional synchronization is required if the state of the object changes after it is published.

  Pattern #3: Independent observation (independent observation)

Another simple model for safe use of volatile is to periodically "publish" observations for use within the program. For example, suppose an ambient sensor can feel ambient temperature. A background thread may read the sensor every few seconds and update the volatile variable that contains the current document. The variable can then be read by other threads so that the latest temperature values can be seen at any time.

Another application that uses this pattern is the collection of statistical information for the program. Listing 4 shows how the authentication mechanism remembers the name of the last logged-on user. The Lastuser primer is used repeatedly to publish values for use by other parts of the program.

Listing 4. Use the volatile variable for multiple independent observations of the publication

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;

}

}

The pattern is an extension of the preceding pattern; a value is published for use elsewhere within the program, but unlike a one-time event, this is a series of independent events. This pattern requires that the values published are valid and immutable-that is, the state of the value does not change after it is published. Code that uses this value needs to be aware that the value may change at any time.

  Mode #4: "Volatile bean" mode

The volatile bean pattern applies to frameworks that use JavaBeans as an "honor structure." In volatile bean mode, JavaBean is used as a container for a set of independent properties with getter and/or setter methods. The rationale for the volatile Bean pattern is that many frameworks provide containers for holders of variable data, such as HttpSession, but the objects placed in these containers must be thread safe.

In volatile bean mode, all data members of the JavaBean are volatile types, and the getter and setter methods must be very common--except to get or set the corresponding properties, they cannot contain any logic. In addition, the referenced object must be valid and immutable for the data member referenced by the object. (This will prohibit properties with array values, because when an array reference is declared as volatile, only the reference, not the array itself, has volatile semantics). For any volatile variable, invariants or constraints cannot contain JavaBean properties. The example in Listing 5 shows the JavaBean that adheres to the volatile bean pattern:

Mode #4: "Volatile bean" mode

@ThreadSafe public

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;

}

}

Advanced Mode of Volatile

The patterns described in the previous sections cover most of the basic use cases, and using volatile in these patterns is useful and simple. This section describes a more advanced pattern in which volatile provides performance or scalability benefits.

The advanced mode of volatile application is very fragile. Therefore, the assumptions must be carefully documented, and these patterns are tightly encapsulated because even very small changes can damage your code! Again, the reason for using a more advanced volatile use case is that it can improve performance and ensure that you really determine the need to achieve this performance before you start applying advanced mode. These patterns need to be weighed against readability or maintainability in exchange for possible performance gains-if you do not need to improve performance (or if you are not able to prove you need it through a rigorous test program), it is likely to be a bad deal because you are likely to lose the benefit What you get is less valuable than what you give up.

Pattern #5: Low cost read-write lock policy

So far, you should know that the volatile feature is not enough to implement the counter. Because ++x is actually a simple combination of three operations (read, add, store), if multiple threads are coincidentally trying to perform an incremental operation on the volatile counter, its updated values are likely to be lost.

However, if the read operation goes far beyond the write operation, you can use both internal locks and volatile variables to reduce the overhead of the common code path. The thread-safe counters shown in Listing 6 use synchronized to ensure that incremental operations are atomic and use volatile to guarantee the visibility of the current results. This method can achieve better performance if the update is not frequent, because the cost of the read path involves only the volatile read operation, which is usually better than the overhead of a less-competitive lock acquisition.

Listing 6. Implement "low cost read-write lock" with volatile and synchronized

@ThreadSafe public

class Cheesycounter {

//employs the cheap read-write lock trick

/All mutative oper  Ations must is done with the ' this ' lock held

@GuardedBy ("This") private volatile int value;

public int GetValue () {return value;}

public synchronized int increment () {return

value++;

}

}

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 case violates the first condition of using volatile, you cannot use volatile to implement the counters securely-you must use a lock. However, you can use volatile in a read operation to ensure the visibility of the current value, so you can use the lock to do all the changing operations, 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 a read operation, so when using volatile to guarantee the read code path, a higher degree of sharing is achieved than using a lock to execute all the code paths-like a read-write operation. Keep in mind, however, the weaknesses of this pattern: if you go beyond the most basic application of the model, it will be very difficult to combine the two competing synchronization mechanisms.

On order reordering and Happens-before rules

1. Order reordering

The Java language Specification stipulates that the JVM thread maintains sequential semantics internally, meaning that as long as the end result of the program is equivalent to its result in a strictly sequential environment, the order of instruction execution may be inconsistent with the order of the Code. This process is sorted by a reordering called instructions. The meaning of instruction reordering is that the JVM can properly reorder machine instructions according to the characteristics of the processor (the CPU's multilevel caching system, multi-core processor, etc.), so that the machine instructions are more in line with the performance of the CPU, and the machine is maximized.

The simplest model of program execution is the order in which the instructions appear, which is independent of the CPU that executes the instruction, and guarantees the portability of the instruction to the maximum. The professional term for this model is called sequential consistency model. But neither the modern computer system nor the processor architecture guarantee this (since human designations do not always guarantee the characteristics of CPU processing).

2. Appens-before Law

The Java storage model has a happens-before principle, that is, if action B is to see the result of action a (whether or not A/b is executing in the same thread), A/s needs to satisfy the happens-before relationship.

Before introducing the Happens-before law, we introduce a concept: JMM action (Java Memeory Model action), Java Storage Modeling action. One action (action) includes reading and writing of variables, monitor locking and releasing locks, thread start (), and join (). The lock is also mentioned later.

  Happens-before Complete rule:

(1) Each action in the same thread is happens-before to any action that appears thereafter.

(2) Unlocking a monitor happens-before each subsequent pair of lock on the same monitor.

(3) The write operation on the volatile field happens-before to each subsequent read operation of the same field.

(4) The call to Thread.Start () is happens-before the action in the boot thread.

(5) All actions in thread are happens-before to other threads to check to the end of this thread or thread.join () or thread.isalive () ==false.

(6) a interrupt () of one thread A calling another thread B is happens-before to thread a discovering that B is interrupted by a (b throws an exception or a detects a isinterrupted () or interrupted () of B).

(7) The end happens-before of an object constructor and the beginning of the finalizer of the object

(8) If a action Happens-before to B action, and B action Happens-before and C action, then a action happens-before in C action.

The above is the entire content of this article, about the volatile variable for everyone to introduce here, I hope to learn more about Java in the volatile variable help.

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.