Java concurrency Programming (ii) object invariant and secure publication object

Source: Internet
Author: User
Tags visibility volatile

First, invariance

Another way to meet synchronization requirements is to use immutable objects (immutable object).

So far, we've covered a lot of issues related to atomicity and visibility, such as getting failure data. Loss of update operations or the presence of an object in an inconsistent state, and so on, are related to the same mutable state as the multithreaded view at the same time. Assuming that the state of an object does not change, then these problems and complexities naturally disappear.

If an object is created and its state cannot be changed, then the object becomes immutable . Thread-Safe is one of the intrinsic properties of immutable objects, and their invariance conditions are created by constructors , and the invariant conditions can be maintained only if their state does not change.

Immutable objects are very easy. They have only one state, and the state is controlled by a constructor function . One of the most difficult parts of programming is to infer the possible state of complex objects. However, it is very easy to infer the state of immutable objects.

Although there is no formal definition of immutability in the Java specification and in the Java memory model, immutability does not mean that all fields in the object are declared as final, even if all of 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 domain of type

The object is immutable when the following conditions are met:

    • The state cannot be changed 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 Threestooges to see. Although the set object that holds the name is mutable, it can be seen from the design of the threestooges. The set object cannot be altered after it has been constructed. Stooges is a reference variable of the final type, so all object states are interviewed through a final domain. The last requirement is to "construct the object correctly". This requirement is very easy to satisfy, because the constructor allows the reference to be visited by code other than the constructor and its callers.

Because the state of the program is constantly changing, you may feel that there are not many places where you need to use immutable objects. But the reality is not.

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

Keyword final can be considered a restricted version number of the const mechanism in C + + for constructing immutable objects. The final type of field is immutable (but assuming that 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. This allows unrestricted access to immutable objects and eliminates the need for synchronization when sharing these objects.

Note: as a personal understanding, the final field once initialized. And the constructor does not pass the this reference, the value of the final field can be seen in other threads (intra-domain variable visibility, similar to volatile), 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, declare some fields of the object to be of the final type. Can still simplify the inference of state . Therefore, restricting the variability of an object is equivalent to restricting the possible set of States for that object. A "basic non-volatile" object that includes only one or two mutable states is still simpler than an object that includes multiple mutable states. 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, "it is a good habit to declare a domain unless it needs to be mutable."

Demo Sample: Use Volatile types to advertise immutable objects

As we have said before, volatile can be used to guarantee the visibility of the domain without guaranteeing the atomicity of the variable operation, more accurately. It is only guaranteed that the read-write operation is atomic, but not the atomicity of the arithmetic operation such as 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 is not a thread-safe approach, Since we cannot atomically read or update these two related values at the same time. Same. It is also 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 runs two atomic operations: updates the results of the cache, and determines whether to read the factorization results directly from the cache by inferring whether the values in the cache are equal to the requested values.

Whenever you need to run an operation atomically on a set of related data, you can consider creating an immutable class to include the data, such as Onevaluecache.

@Immutableclass Onevaluecache {    private final BigInteger lastnumber;    Private final biginteger[] lastfactors;    /**     * assumes that the arrays.copyof () method is not used in the constructor. Then the immutable object Lastfactors in 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);}    }

The problem of race conditions that arise when interviewing and updating multiple related variables can be eliminated by saving all of these variables in an immutable object.

If you are a mutable object, you must use locks to ensure atomicity.

Suppose it is an immutable object, then when a thread obtains a reference to the object, it does not have to worry about a thread that changes the state of the object . Suppose you want to update these variables. It is possible to create a new container object. However, 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);//is declared volatile. Prevent command reordering, guaranteed visibility        }        encodeintoresponse (resp, factors);}    }

Cache-related operations do not interfere with each other. Because the Onevaluecache is immutable. And only visit it once in each corresponding code path. The invariance condition is maintained by using a container object that includes multiple state variables. and use a volatile type of reference to ensure visibility, so that volatile Cached Factorizer is still thread-safe without explicitly using locks.


II. Security Announcement

So far, we have focused on how to make sure that objects are not published, such as enclosing objects in the thread or inside an 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 advertise this object, assuming that the object reference is saved to the public domain just like the following program.

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

You might be surprised. This seemingly non-problematic demo sample fails to execute.

Because of a visibility issue, the holder object that other threads see will be in an inconsistent state, even if the invariant condition has been correctly constructed in the object's constructor. This incorrect announcement causes other threads to see objects that have not yet been created.

Wrong announcement: The right object is destroyed

You cannot expect an object that has not yet been completely created to have integrity. A thread that observes the object will see the object in an inconsistent state. You can then see that the state of the object suddenly changes, even though the thread has not changed it after the object has been advertised. In fact, assuming that Holder in the following program uses unsafe advertisement in the previous program, there is one thread that 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 advertised properly." There are two issues in objects that are not advertised correctly.

first , in addition to the thread that publishes the object, the holder domain that other threads can see is an invalid value . Therefore, 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. What becomes even more unpredictable is that a thread gets a stale value the first time it reads the domain, and then reads the domain again with an updated value. This is also the reason why Assertsainty throws Assertionerror.

Assuming there is not enough synchronization, something strange happens when you share data among multiple threads.

Immutable objects and initialization security

Because immutable objects are an 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.

There is one more aspect. Even if you do not use synchronization when you advertise a reference to an immutable object, you are still able to safely access the object.

In order to maintain such a guarantee of initialization security. All the requirements for immutability must be met: The state cannot be changed, and all fields are final types. And the correct construction process. ( assuming that the holder object is immutable, even if the holder is not properly advertised.) Assertionerror are not thrown in assertsanity.

No matter what thread can safely access non-changing objects without the need for additional synchronization, even when the objects are advertised, no synchronization is used.

This guarantee will also extend to the fields that are correctly created for all final types in the object. In the absence of additional synchronization, it is also safe to access the final type of domain. However. Assume that a field of the final type points to a mutable object. You will still need to synchronize when visiting the state of the objects to which these fields point.

Regular usage patterns for security announcements

Mutable objects must be advertised in a secure manner, which usually means that synchronization must be used when the thread of the object is advertised and used.

Today, we'll focus on how to make sure that the thread that uses the object can see that the object is in the advertised state. and later on how to change the visibility of an object after it is published.

Safely publish an object. The application of the object and the state of the object must be visible to other threads at the same time. A properly constructed object can be safely advertised 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 the thread security container means that the object is placed in a container. such as vectors or synchronizedlist, will meet the last requirement above. Assume that thread a puts object x into a thread-safe container. Then thread B reads the object, which ensures that B sees the X state of a set, even if explicit synchronization is not included in the application code for this read/write X. Although Javadoc does not give a very clear explanation on this topic, the container classes in the Thread Safety Library provide the following security disclosure guarantee:
    • By placing a key or value in Hashtable, Synchronizedmap, or concurrentmap, it is safe to advertise it to any thread that visits it from those containers (whether it is a direct interview or an iterator)
    • By placing an element in a vector, copyonwritearraylist, Copyonwritearrayset, synchronizedlist, or Synchronizedset, The ability to safely advertise this element to whatever thread is visiting the element from these containers
    • By putting an element into Blockingqueue or concurrentlinkedqueue, you can safely advertise that element to whatever thread is visiting the element from those queues.

Other data-passing mechanisms in the class library, such as future and exchanger, can be implemented as security disclosures. These mechanisms will be discussed when they are described in the security Announcement feature.

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

A static initializer is run by the JVM during the initialization phase of the class.

Because there is a synchronization mechanism within the JVM, any object initialized in this way can be safely advertised [JLS 12.4.2].

Fact Non-mutable object

Assuming that objects are not altered after they are published, it is sufficient for other threads to safely access these objects without additional synchronization. All of the security announcement mechanisms are guaranteed. When a reference to an object is visible to all the threads that access the object, the state of the object's publication will be visible to all threads, and assuming that the object state is no longer changed, it is sufficient to ensure that any access is safe.

Suppose the object is technically mutable, but its state will not change after it is published. So the object is called the Fact immutable object (effectively immutable object). These objects do not need to meet the strict definition of immutability that was previously proposed. After these objects are published. Programs only need to treat them as immutable objects. The object is not mutable by using the fact. Not only simplifies the development process, but also improves performance due to reduced synchronization.

In the absence of additional synchronization, it is safe to use the non-volatile objects of the fact that are safely advertised, regardless of the thread.

For example, date itself is mutable, but if it is used as an immutable object, the use of locks can be omitted when a Date object is shared among multiple threads.

If you need to maintain a map object, it saves each user's recent logon time:
Public map<string, date> lastlogin =collections.synchronizedmap (New hashmap<string, Date> ());

Assuming that the value of the Date object does not change after being placed in the map, the synchronization mechanism in Synchronizedmap is sufficient to make the date value publicly available. And there is no need for additional synchronization when visiting these date values.

mutable objects

Assuming that the object can be altered after construction, the security announcement will only ensure visibility of the "published" state. For mutable objects, you need to use synchronization not only to advertise objects, but also to use synchronization every time an object is interviewed to ensure that the visibility of the operation may be altered.

To safely share mutable objects. These objects must be safely published. and must be thread-safe or protected by a lock.

The advertisement requirements of an object depend on its variability:

    • Immutable objects can be advertised by random mechanisms.
    • The facts are immutable and must be published in a secure manner
    • Mutable objects must be advertised 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 operations can be run on that reference.

Do I need to get a lock before using it? Can you change the state of it, or just 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 clearly state how the object is visited.

Java concurrency Programming (ii) object invariant and secure publication object

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.