Java concurrency Programming (ii) invariant and secure publishing objects for objects

Source: Internet
Author: User
Tags visibility

First, invariance

Another way to meet synchronization requirements is to use immutable objects (immutable object). So far, we've covered a number of issues related to atomicity and visibility, such as getting stale data, losing update operations or having an object in an inconsistent state, and so on, all associated with accessing the same mutable state at the same time as the multithreaded view. If the state of the object does not change, then these problems and complexity will naturally disappear.

If an object is created and its state cannot be modified, then the object becomes immutable . Thread-Safe is one of the intrinsic properties of immutable objects, and their invariant conditions are created by constructors , so long as their state does not change, then these invariant conditions can be maintained.

Immutable objects are simple. They have only one state, and the state is controlled by the constructor . One of the hardest places in programming is to judge the possible state of a complex object. However, it is simple to judge the state of immutable objects.

Although there is no formal definition of immutability in both the Java specification and the Java memory model, immutability does not mean that all fields in the object are declared as final, even if all the fields in the object are of the final type, the object is still mutable because the final A reference to a Mutable object can be saved in a field of type.

An object is immutable when the following conditions are true:

    • The state cannot be modified after the object is created
    • All and all of the objects are final types
    • object is created correctly (no escape of this during creation)
Let's analyze the following class.
@Immutablepublic Final class Threestooges {    private final set<string> stooges = new Hashset<string> (); C2/>public threestooges () {        stooges.add ("Moe");        Stooges.add ("Larry");        Stooges.add ("Curly");    }    public boolean isstooge (String name) {        return stooges.contains (name);}    }

Mutable objects can still be used inside immutable objects to manage their state, as shown in Threestooges. Although the set object that holds the name is mutable, you can see from the design of the threestooges that it cannot be modified after the set object is constructed. Stooges is a reference variable of the final type, so all object states are accessed through a final domain. The last requirement is to "properly construct the object", which is easily satisfied because the constructor enables the reference to be accessed by code other than the constructor and its callers.

Since the state of the program is constantly changing, you may think that there is not much place to use immutable objects, but this is not the case. There is a difference between immutable objects and immutable object references. The state of the program saved in the immutable object can still be updated by replacing the existing immutable object with an instance that holds the new state.

Final Field

The keyword final can be considered a restricted version of the const mechanism in C + + for constructing immutable objects. A final type of field cannot be modified (but if the object referenced by the final field is mutable, these referenced objects can be modified). However, in the Java memory model, the final domain also has a special semantics. The final domain ensures the security of the initialization process, allowing unrestricted access to immutable objects and eliminating the need for synchronization when sharing those objects.

Note: as a personal understanding, once the final field is initialized and the constructor does not pass the this reference, the value of the final field (in-domain variable visibility, similar to volatile) can be seen in other threads, and its externally visible state will never change. The security that it brings is the simplest and purest.

Note: Even if the object is mutable, by declaring certain fields of an object as final, you can still simplify the judgment of the state, so limiting the variability of the object is equivalent to restricting the possible set of States of the object. A "basic non-volatile" object that contains only one or two mutable states is still simpler than an object that contains more than one mutable state. By declaring a domain as the final type, it is also equivalent to telling the maintainer that these fields will not change.

Just as "unless you need more visibility, you should declare all the hungry domains as private domains" [EJ Item 12] is a good habit to become, "unless a domain is mutable, it should be declared as a final field" is also a good habit.

Example: Using Volatile types to publish immutable objects

As we have said before, volatile can be used to guarantee the visibility of the domain and not to guarantee the atomicity of the variable operation, more accurately, can only guarantee that the read and write operations have atomicity, and can not guarantee the atomic nature of the operation of the self-increment i++.

In the previous Unsafecachingfactorizer class, we tried to use two atomicreferences variables to save the latest values and their factorization results, but this was not thread-safe. Because we cannot atomically read or update these two related values at the same time. Similarly, it is not thread-safe to store these values with variables of volatile type. However, in some cases, immutable objects can provide a weak form of atomicity.

The factorization Servlet performs two atomic operations: updates the results of the cache, and determines whether to directly read the factorization results in the cache by determining whether the values in the cache are equal to the requested values. Whenever you need to perform an operation atomically on a set of related data, consider creating an immutable class to contain the data, such as Onevaluecache.

@Immutableclass Onevaluecache {    private final BigInteger lastnumber;    Private final biginteger[] lastfactors;    /**     * If the arrays.copyof () method is not used in the constructor, the immutable object lastfactors within the domain can be changed by the extraterritorial code     * Then Onevaluecache is not immutable.     *    /Public Onevaluecache (BigInteger I,                         biginteger[] factors) {        Lastnumber  = i;        Lastfactors = arrays.copyof (factors, factors.length);    }    Public biginteger[] Getfactors (BigInteger i) {        if (Lastnumber = = NULL | |!lastnumber.equals (i))            return null;< C15/>else            return arrays.copyof (lastfactors, lastfactors.length);}    }

for race condition problems that occur when accessing and updating multiple dependent variables, you can eliminate them by saving them all in an immutable object. In the case of a mutable object, a lock must be used to ensure atomicity. If it is an immutable object, then when the thread obtains a reference to the object, it you don't have to worry about another thread modifying the state of an object. If you want to update these variables, you can create a new container object, but other threads that use the original object will still see the object in a consistent state.

In Volatilecachedfactorizer, Onevaluecache is used to save the cached values and their factors. We declare onevaluecache as volatile so that when a thread sets the cache to reference a new Onevaluecache, the other threads immediately see the newly cached data.

@ThreadSafepublic class Volatilecachedfactorizer implements Servlet {    private volatile onevaluecache cache =        New Onevaluecache (null, NULL);    public void Service (ServletRequest req, Servletresponse resp) {        BigInteger i = extractfromrequest (req);        biginteger[] factors = cache.getfactors (i);        if (factors = = NULL) {            factors = factor (i);            cache = new Onevaluecache (i, factors);//Declaration as volatile, prevent command reordering, guaranteed visibility        }        encodeintoresponse (resp, factors);}    }

Cache-related operations do not interfere with each other, because Onevaluecache is immutable and only accesses it once in each corresponding code path. The volatile Cached Factorizer is thread-safe without explicitly using a lock by using a container object that contains multiple state variables to maintain the invariant condition and use a volatile type reference to ensure visibility.

Second, security release

So far, we have focused on ensuring that objects are not published, such as enclosing an object inside a thread or another object. Of course, in some cases we want to share objects across multiple threads, and we must be sure to share them securely. However, it is not enough to safely publish an object reference if it is simply saved to the public domain as in the following program.

Unsafe publishing public Holder holder;public Void Initialize () {    Holder = new Holder (42);}

You might wonder why this seemingly non-problematic example would fail to run. Because of a visibility issue, the holder object that other threads see will be in an inconsistent state even though the invariant condition has been correctly constructed in the object's constructor. This incorrect publication causes other threads to see objects that have not yet been created.

Incorrect publication: The correct object is destroyed

You cannot expect an object that has not yet been completely created to have integrity. A thread that observes the object sees the object in an inconsistent state, and then sees that the state of the object suddenly changes, even if the thread has not modified it after the object has been published. In fact, if holder in the following program uses unsafe publishing in the previous program, then another thread will throw assertionerror when calling Assertsanity.

public class Holder {    private int n;    public Holder (int n) {THIS.N = n;}    public void Assertsanity () {        if (n! = N)            throw new Assertionerror ("this statement is false.");}    }

Because synchronization is not used to ensure that the holder object is visible to other threads, holder is called "not published correctly." There are two issues in objects that are not published correctly.

first , in addition to the thread that publishes the object, the holder field that other threads can see is an invalid value , so you will see a null reference or a previous old value.

Worse, however , the thread sees that the value referenced by the holder is up to date, but the value of the holder state is invalidated. It becomes even more unpredictable that a thread gets an invalid value when it first reads the domain, and when it reads the domain again it gets an updated value, which is why Assertsainty throws Assertionerror.

If there is not enough synchronization, some very strange things will happen when you share data among multiple threads.

Immutable objects and initialization security

Because immutable objects are a very important object, the Java memory model provides a special type of initialization security guarantee for the sharing of immutable objects . We already know that even if a reference to an object is visible to other threads, it does not mean that the object state is necessarily visible to the thread that is using the object. To ensure that the object state renders a consistent view, you must use synchronization.

On the other hand, even when you publish a reference to an immutable object without using synchronization, you can still access the object safely. To maintain this security of initialization, all requirements for immutability must be met: The state cannot be modified, all fields are final, and the correct construction process. ( If the holder object is immutable, Assertionerror is not thrown in assertsanity even if the holder is not published correctly.) )

Any thread can safely access non-changing objects without requiring additional synchronization, even if they are published without using synchronization.

This guarantee will also extend to the fields of all final types that are correctly created in the object. In the absence of additional synchronization, it is also safe to access the final type of domain. However, if the final type of field points to a Mutable object, synchronization is still required to access the state of the object to which the fields point.

Common patterns for Secure publishing

Mutable objects must be published in a secure manner, which usually means that synchronization must be used when the thread of the object is published and used. Now we'll focus on making sure that the thread that uses the object can see that the object is in a published state, and later on how to modify the visibility of the object after it is published.

Securely publish an object, the object's application, and the state of the object must be visible to other threads at the same time. A properly constructed object can be safely published in the following ways:

    • Initializing an object reference in a static initialization function
    • Save an object's app to a volatile type of field or Atomicreferance object
    • Saves a reference to an object in the final type field of a properly constructed object
    • Saves a reference to an object in a lock-protected domain.
Synchronization within a thread-safe container means that the last requirement above is met when the object is placed into a container, such as a vector or synchronizedlist. If thread a puts object x in a thread-safe container and then thread B reads the object, you can make sure B sees the X state of the A setting, even if there is no explicit synchronization in the application code for this read/write X. Although Javadoc does not give a clear explanation on this topic, the container classes in the Thread Safety Library provide the following security release guarantees:
    • By placing a key or value in Hashtable, Synchronizedmap, or concurrentmap, you can safely publish it to any thread that accesses it from those containers (whether accessed directly or through an iterator)
    • By placing an element in a vector, copyonwritearraylist, Copyonwritearrayset, synchronizedlist, or Synchronizedset, This element can be safely published to any thread that accesses the element from these containers
    • By putting an element into Blockingqueue or concurrentlinkedqueue, you can safely publish the element to any thread that accesses the element from those queues.

Other data-passing mechanisms in the class library, such as future and exchanger, also enable secure publishing, which is discussed in the security publishing capabilities of these mechanisms.

In general, the simplest and safest way to publish a statically constructed object is to use a static initializer:
public static Holder Holder = new Holder (42);

Static initializers are performed by the JVM during the initialization phase of the class. Since there is a synchronization mechanism within the JVM, any object initialized in this manner can be safely published [JLS 12.4.2].

Fact Non-mutable object

If an object is not modified after it is published, it is sufficient for other threads that securely access these objects without additional synchronization. All security release mechanisms ensure that when an object's reference is visible to all threads accessing the object, the state of the object's publication will be visible to all threads, and if the state of the object is no longer changed, it is sufficient to ensure that any access is secure.

If an object is technically mutable, but its state does not change after it is published, this object is called a fact-immutable object (effectively immutable objects). These objects do not need to meet the strict definition of immutability that was previously proposed. After these objects are published, the program simply treats them as immutable objects. By using fact-immutable objects, you can not only simplify the development process, but also improve performance due to reduced synchronization.

In the absence of additional synchronization, any thread can safely use the fact immutable object that is released safely.

For example, the date itself is mutable, but if it is used as an immutable object, then the use of the lock can be omitted when the date object is shared among multiple threads. Suppose you need to maintain a map object that holds the most recent logon time for each user:
Public map<string, date> lastlogin =collections.synchronizedmap (New hashmap<string, Date> ());

If the value of the Date object does not change after it is placed in the map, then the synchronization mechanism in Synchronizedmap is sufficient to make the date value published safely, and no additional synchronization is required to access these date values.

mutable objects

If an object can be modified after it is constructed, the security release can only ensure visibility of the "publish at that time" state. For mutable objects, you need to use synchronization not only when you publish an object, but also with synchronization every time you access the object to ensure the visibility of subsequent modifications. To safely share mutable objects, these objects must be securely published and must be thread-safe or protected by a lock.

The publication requirements of an object depend on its variability:

    • Immutable objects can be published by any mechanism.
    • Fact immutable must be published in a secure manner
    • Mutable objects must be published in a secure manner and must be thread-safe or protected by a lock
Secure shared objects

When you get a reference to an object, you need to know what can be done on that reference. Do I need to get a lock before using it? Is it possible to modify its state, or can it only read it? Many concurrency errors are caused by the lack of understanding of these "established rules" of shared objects. When you publish an object, you must explicitly describe how the object is accessed.

Java concurrency Programming (ii) invariant and secure publishing objects for objects

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.