Java memory model and Common keywords for concurrent synchronization (volatile and synchronized) __java

Source: Internet
Author: User
Tags visibility volatile
1.Java Memory Model

Reference: Http://www.infoq.com/cn/special-column/articles/Java memory model Column 2.volatile understanding of keywords

The volatile variable in the Java language can be viewed as a "lighter synchronized", and the volatile variable requires less coding and less run-time overhead than the synchronized block, but the functionality it can achieve is only Part of the synchronized. This article describes several patterns that effectively use volatile variables and emphasizes several scenarios that are not suitable for use with volatile variables.

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.

Volatile variable

Volatile variables have synchronized visibility properties, but do not have atomic properties. This means that the thread can automatically discover the most recent value of the volatile variable. The Volatile variable can be used to provide thread safety, 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. Therefore, using volatile alone is not enough to implement a counter, a mutex, or any class that has a invariant (invariants) associated with multiple variables (for example, "Start <=end").

For simplicity or scalability reasons, you might prefer to use volatile variables instead of locks. When using volatile variables rather than locks, some idioms (idiom) are easier to encode and read. In addition, the volatile variable does not cause a thread to block like a lock, so it rarely causes scalability problems. In some cases, if the read operation is much larger than the write operation, the volatile variable can provide a performance advantage over the lock.

Conditions for proper 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: write operations on variables do not depend on the current value. The variable is not included in the invariant with other variables.

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 two 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.
Listing 1. Non-thread-safe numeric range classes

<span style= "FONT-SIZE:18PX;" >                
@NotThreadSafe Public 
class Numberrange {
    private int lower, upper;

    public int Getlower () {return lower;}
    public int Getupper () {return upper;}

    public void Setlower (int value) { 
        if (Value > Upper) 
            throw new IllegalArgumentException (...);
        lower = value;
    }

    public void Setupper (int value) { 
        if (value < lower) 
            throw new IllegalArgumentException (...);
        upper = value;
    }
}
</span>

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 class, and thus still requires synchronization. Otherwise, if it happens that two threads execute Setlower and setupper at the same time using inconsistent values, the scope is in an inconsistent state. For example, if the initial state is (0, 5, at the same time, thread A calls Setlower (4) and thread B calls Setupper (3), it is obvious that these two operations are not eligible for the cross deposit, then two threads will be used to protect the invariant type of check, so that the final range value is (4 , 3)--An invalid value. As for the other operations on the scope, we need to make setlower () and Setupper () operations atomized-and it's not possible to define fields as volatile types.

Performance considerations

The main reason for using volatile variables is simplicity: In some cases, using the volatile variable is much simpler than using the appropriate lock. 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 an accurate, comprehensive evaluation, such as "X is always faster than Y", especially for the internal operations of the JVM. (for example, in some cases the VM may be able to completely remove the lock mechanism, which makes it difficult to abstract the cost of volatile and synchronized.) That is, on most current processor architectures, the overhead of volatile reads is very low-almost as much as a volatile read operation. The cost of volatile writes is much higher than that of volatile writes, as the total cost of volatile is still lower than lock acquisition to ensure that visibility requires a memory definition (Memory Fence).

Volatile operations do not cause blocking like locks, so volatile can provide some of the scalability features that are superior to locks in situations where volatile can be safely used. If the number of read operations is much higher than the write operation, volatile variables typically reduce the performance overhead of synchronization compared to locks.

Correct use of volatile mode

Many concurrent experts actually tend to steer users away from volatile variables, because they are more prone to error 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-volatile--this rule can avoid extending these patterns to unsafe use cases only if the state is truly independent of other content within the program.

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

<span style= "FONT-SIZE:18PX;" >                
Volatile boolean shutdownrequested;

...

public void shutdown () {shutdownrequested = true;}

public void DoWork () {while 
    (!shutdownrequested) { 
        //do Stuff
    }
}
</span>

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 famous double check lock (double-checked-locking) problem where the object reference is read without synchronization, and the problem is that you may see an updated reference, but still see an incomplete constructed object through that 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

<span style= "FONT-SIZE:18PX;" > Public                
class Backgroundfloobleloader {public
    volatile flooble theflooble;

    public void Initinbackground () {
        //do lots of stuff
        theflooble = new flooble ();  This is the "write to Theflooble
    }" public

class SomeOtherClass {public
    void DoWork () {
        whi Le (True) { 
            //do some stuff
            ... Use the flooble, but only if it is ready
            if (floobleloader.theflooble!= null) 
                dosomething (Floobleloader.thef looble);
}} </span>

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

<span style= "FONT-SIZE:18PX;" > 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
    }
} 
</span>

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:
Listing 5. The person object that adheres to the volatile bean pattern

<span style= "FONT-SIZE:18PX;" >                
@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;
    }
}
</span>

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

<span style= "FONT-SIZE:18PX;" >                
@ThreadSafe Public
class Cheesycounter {
    //employs the cheap read-write lock trick
    /All mutative O  Perations must is done with the ' this ' lock held
    @GuardedBy ("This") private <strong>volatile</strong> int value;

    public int GetValue () {return value;}

    public <strong>synchronized</strong> int Increment () {return
        value++;
    }
}
</span>

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.

Conclusion

Compared to locks, the Volatile variable is a very simple, yet very fragile, synchronization mechanism that in some cases provides better performance and scalability than locks. If you strictly follow the volatile conditions-that is, the variables are truly independent of other variables and their previous values-in some cases you can use volatile  instead of  synchronized  to simplify the code. However, the use of  volatile  code is often more error-prone than code that uses locks. The patterns described in this article cover some of the most common use cases where you can use volatile  instead of  synchronized . Following these patterns (note that you do not exceed their limits when you use them) can help you achieve most of the use cases securely, using volatile variables for better performance.

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.