Designing a thread-safe class--a combination of objects

Source: Internet
Author: User
Tags closure

Some combination patterns make it easier for a class to be thread-safe and easy to maintain. Avoid complex memory analysis to ensure that threads are secure.

Designing a thread-safe class to report the following three elements:

1. Find out all the variables that make up the state of the object.

All the fields of the object make up the state of the object. If the field of an object is a basic variable, then these fields form the entire state of the object. If another object is referenced in the object's domain, the object's state also contains the domain of its reference object. A state such as ArrayList contains the state of all its node objects.

2. Find the invariant condition of constraint state variable

3. Establish concurrent access policy for object state.

The invariant, thread blocking, locking mechanism are combined to maintain thread security.

Collect synchronization requirements

To ensure thread safety, you need to ensure that the invariant condition is no longer compromised in concurrent access. Both the object and the variable have a state space, which is all possible values. The smaller the state space, the easier it is to determine the thread state.

 Public Final classcounter{Private LongValue = 0;  Public synchronized LongGetValue () {returnvalue; }     Public synchronized Longincrement () {if(Value = =long.max_value)Throw Newillegalstateexception (); return++value; }}

Immutable conditions are defined in many classes to determine whether the state is valid. If value in counter is a variable of type long, the range of values is long.min_value to Long.max_value, but there is also a limitation that value cannot be a negative value.

In the class, it is also possible to migrate whether the status of a posteriori condition class is valid. If the current state of counter is 17, then the next valid state is 18. When the next state depends on the current state, then this operation is in accordance with the operation. Not a soy state transition operation has a posteriori condition, such as updating the temperature, and does not depend on the previous result.

Because invariants and post conditions impose constraints on state and state transitions, additional synchronization and encapsulation are required, or the client code may be an object in an invalid state. If there is an invalid state transition, it must be an atomic operation.

For invariant conditions that contain multiple state variables, you need to read and update in an atomic operation, you cannot first update a variable and then release the lock, and then update the other variables. Because the lock is released, the object may be in an invalid state.

To ensure the thread safety of an object, it is necessary to understand the invariant and posterior conditions of an object, with the help of atomicity and encapsulation.

Prior conditions (pre-condition), posterior conditions (post-condition), invariance conditions (invariant) have the following meanings:

    1. Pre-conditions was the things that must was true before a method is called. The method tells clients "This was what I expect from you".
    2. Post-conditions was the things that must was true after the method was complete. The method tells clients "This was what I promise to do for you".
    3. Invariants is the things that is always true and won ' t-change. The method tells clients "If this is true before you called me, I promise it ' ll still is true when I'm done".
Actions that depend on state

Some objects have methods that contain state-based prior conditions and cannot remove an element from an empty queue. In a single thread, a priori condition cannot be satisfied, and the operation fails. However, in concurrent programs, operations that do not satisfy a priori condition are executed successfully because of the operation of other threads.

Instance closure

For a non-thread-safe object, you can use some technology to make it safe for multi-threading. For example, make sure that the object is accessed single-threaded or protected by a lock.

Encapsulation simplifies the implementation of thread-safe classes, which provides an instance closure mechanism. When an object is encapsulated in another object, all code that accesses the object is known, and the encapsulated object is easier to parse than the object accessed by the entire program. The closed object must not exceed the defined scope.

Enclosing data inside an object allows data access to be restricted to object methods, making it easier to ensure that the thread is holding the correct lock when it accesses the data.

 Public class personset{    privatefinalnew hashset<>();      Public synchronized Long addpersion (Persion p) {        myset.add (p);    }      Public synchronized Boolean containspersion (Persion p) {        return  myset.contains (p);    }}

HashSet is not thread-safe, but hashset is enclosed in persionset, and the only code that can access MySet is protected by locks. So Persionset is thread-safe.

In this case, the thread safety of persion is not assumed. If the persion is mutable, then access to persion requires additional synchronization.

There are also many similar thread-enclosing classes in the Java class Library, such as Collections.synchronizedlist and its similar methods, where the only purpose of these classes is to convert a non-thread-safe class into a thread-safe class.

Java Monitor mode

Java's built-in lock is called a monitor lock or monitor, encapsulates all mutable objects, and has the object's own built-in lock protection. Belongs to the instance closure. The previous counter example is this pattern. You can also protect objects with a private lock, avoid client code acquiring locks, create deadlock problems, and simply check a single class to verify that the lock is used correctly.

Example: Vehicle tracking

classmutablepoint{ Public  intx, y;  PublicMutablepoint () {x=0; Y=0; }     PublicMutablepoint (Mutablepoint point) { This. x =Point.x;  This. y =Point.y; }} Public classmonitorvehicletracker{Private FinalMap<string, mutablepoint>locations;  PublicMonitorvehicletracker (map<string, mutablepoint>locations) {         This. Locations =locations; }     Public synchronizedMap<string, mutablepoint>getlocations () {returndeepcopy (locations); }     Public synchronizedmutablepoint getLocation (String id) {Mutablepoint loc=locations.get (ID); returnLOC = =NULL?NULL:Newmutablepoint (Loc); }     Public synchronized voidsetlocation (String ID,intXinty) {Mutablepoint loc=locations.get (ID); if(loc = =NULL)            Throw Newillegalstateexception (); loc.x=x; Loc.y=y; }    Private StaticMap<string, mutablepoint> deepcopy (map<string, mutablepoint>m) {Map<string, mutablepoint> locs =NewHashmap<>();  for(String id:m.keyset ()) {Mutablepoint loc=NewMutablepoint (M.get (id));        Locs.put (ID, loc); }        returnCollections.unmodifiablemap (locs); }}

Suppose each car has a string object to mark, and a positional coordinate (x, y). Read the location through a thread, display it, vehicles.getlocations ()

Other threads are responsible for updating the vehicle's location. Vehicles.setlocation (ID, x, y);

Because of concurrent access, which must be thread-safe, monitor mode is used to ensure thread security. Although Mutablepoint is not thread-safe, a mutable point is not published. When returning to the vehicle location, the current position is copied by means of the Deepcopy method. So Monitorvehicletracker is thread-safe. Maintain thread safety by replicating variable data classes. There may be problems, such as performance issues, that do not reflect the location of the vehicle in real time because the snapshot is returned.

Thread-Safe delegation

If the individual components in a class are thread-safe, do you need an additional layer of thread safety? Need to see the situation. In some cases, a class composed of thread-safe classes is thread-safe, called a delegate for thread safety.

Change the vehicle tracker instance to the following code:

classpoint{ Public Final intx, y;  PublicPoint (intXinty) {         This. x=x;  This. y=y; }} Public classmonitorvehicletracker{Private FinalConcurrenthashmap<string, point>locations; Private FinalMap<string, point>Unmodifiablemap;  PublicMonitorvehicletracker (map<string, point>locations) {         This. Locations =NewConcurrenthashmap<>(locations); Unmodifiablemap= Collections.unmodifiablemap ( This. Locations); }     PublicMap<string, point>getlocations () {returnUnmodifiablemap; }     Public voidsetlocation (String ID,intXinty) {        if(Locations.replace (ID,NewPoint (x, y)) = =NULL)            Throw Newillegalstateexception (); }}

We just change the original mutable Mutablepoint class into immutable poient, the immutable values are freely shared and published, so the locattion returned does not need to be duplicated. is managed using thread-safe concurrenthashmap, so there is no synchronization using the display, and thread safety is ensured. Delegate thread safety to Concurrenthashmap.

Delegate to a separate state variable

Above, we are all state variables that are delegated to a single thread-safe. We can also delegate to multiple state variables, but these variables must be independent of each other, that is, the combined class has no invariant conditions on multiple state variables.

Delegate invalidation

Some invariant conditions exist for most composite objects. Will cause the delegate to fail, non-thread safe.

 Public classnumberrange{//Lower <= Upper    Private FinalAtomicinteger lower =NewAtomicinteger (0); Private FinalAtomicinteger upper =NewAtomicinteger (0);  Public voidSetlower (inti) {        if(I >upper.get ())Throw Newillegalargumentexception ();    Lower.set (i); }     Public voidSetupper (inti) {        if(I <lower.get ())Throw Newillegalargumentexception ();    Upper.set (i); }     Public BooleanIsinrange (inti) {        return(I >= lower.get ()) && I <=Upper.get (); }}

Numberrange is not thread-safe, because the operation is performed after the first check, and the operation is not atomic, which destroys the invariant condition that constrains the upper and lower bounds. Both Setlower and Setuper tried to maintain the same conditions, but failed. We can ensure thread safety by locking mechanisms to maintain immutability conditions. As a result, a similar operation can only be thread-safe by delegation.

If a state variable is thread-safe and does not have any invariant conditions to constrain, and there is no invalid state transition, n then it is safe to publish the variable.

Adding functionality to existing thread-safe classes

An atomic operation is added to a thread-safe class, but this is usually not possible because the source code cannot be modified. We can extend this class, such as Bettervector, to extend the vector, add an atomic method, and Putifabsent.

 Public class extends Vector<e>{    publicsynchronizedboolean  putifabsent (E x) {         boolean absent =! contains (x);         if (absent)            Add (x);         return absent;}    }

The above example is thread-safe because the vector exposes the state to the subclass, and the synchronization policy is defined in the specification.

Client lock mechanism

We can add functionality to the thread-safe class in a third way, extending the functionality of the class, not extending the class itself, and putting the extension code in the helper class.

 public  class  listhepler<e>< Span style= "COLOR: #000000" > { public  list<e> List = Collections.sync    Hronizedlist (new  arraylist<e> ());  public  synchronized  boolean   Putifabsent (E x) {  Boolean  absent =!        list.contains (x);         if   (absent) list.add (x);     return   absent; }}

This class appears to be thread-safe, after all, using a synchronous method. However, this is not thread-safe and the problem is that it is synchronized at the wrong lock. Because no matter which lock the list uses to protect the state, it is certainly not a lock on the listhelper. means that Putifabsent's other actions relative to the list are not atomic.

In order for this method to execute correctly, the list must use the same lock when implementing the client lock. The following is the correct example.

 Public class Listhepler<e> {    public list<e> List = collections.synchronizedlist (new  Arraylist<e>());      Public Boolean putifabsent (E x) {        synchronized(list) {            boolean absent =!  List.contains (x);             if (absent)                list.add (x);             return absent;     }}}

Combination

There is also a way to add atomic operations: a combination.

 Public classImprovedlist<e> {    Private FinalList<e>list;  PublicImprovedlist (list<e>list) {         This. List =list; }     Public synchronized Booleanputifabsent (E x) {BooleanAbsent =!list.contains (x); if(absent) list.add (x); returnabsent; }
Pubic sunchronized void Othermethod () {
...
}
}

The client does not directly use the list object, so it does not care whether the list is thread-safe, and Improvedlist adds an extra layer of locks through its own built-in lock. In fact, we used the monitor mode to encapsulate the existing list. Just make sure that the client code does not use list directly to ensure thread safety.

Designing a thread-safe class--a combination of objects

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.