This chapter describes how to share and publish objects so that they can be safely accessed by multiple threads at the same time. Together, these two chapters form an important basis for building thread-safe classes and building the development of concurrent applications through the Java.util.concurrent class library.
3.1 Visibility
Visibility is a complex property, because errors in visibility always violate our intuition. In order to ensure the visibility of memory write operations between multiple threads, the synchronization mechanism must be used.
In the following list, novisibility describes the errors that occur when multiple threads share data without synchronization. The main thread starts the read thread, and then sets number to 42, and the ready is set to true. The read thread loops until the value of ready is found to true and then outputs the value of number. Although it looks like it will output 42, it may actually output 0, or it cannot be terminated at all. This is because the code does not use enough synchronization to ensure that the ready and Nunber values written by the main thread are visible to the read thread.
Public classnovisibility {" frown face-don't do this "Private Static BooleanReady ; Private Static intNumber ; Public Static voidMain (string[] args) {NewReaderthread (). Start (); number= 42; Ready=true; } Private Static classReaderthreadextendsThread { Public voidrun () { while(!Ready ) {Thread.yield (); } System.out.println (number); } }}
Novisibility may continue to cycle because the read thread may never see the ready value. A more bizarre phenomenon is that novisibility may output 0, because a read thread might see writing a ready value, but it does not see that the number value was written before, a phenomenon called " reordering (reordering)". (Note: This may seem like a failed design, but it is a powerful performance that allows the JVM to take full advantage of modern multicore processors .) )
In the absence of synchronization, the compiler, the processor, and the runtime can make some unexpected adjustments to the order in which the operations are executed. In the absence of sufficient synchronization of multi-threaded programs, to determine the execution sequence of memory operations, it is almost impossible to draw the correct conclusion.
3.1.1 Failure data
Novisibility shows a situation in which a program that lacks synchronization can produce incorrect results: failure data . When a read thread looks at the ready variable, it may get a value that has been invalidated. Unless you use synchronization every time you access a variable, you are likely to get a failure value for that variable. Worse, the failure value may not occur at the same time: One program may get the most recent value of a variable, and get the invalid value of another variable.
Failure data can also lead to confusing failures such as unexpected anomalies, corrupted data structures, imprecise computations, and infinite loops .
In the following program manifest Mutableinteger is not thread-safe, because both get and set access value without synchronization. If a thread calls set, the other get thread that is called may see the updated value or it may not be visible.
Public class Mutableinteger { privateint value; Public int get () { return value; } Public void Set (int value) { this. Value = value; }}
In the program Qing Synchronizedinteger, by synchronizing the Get and set methods, the Mutableinteger can be made a thread-safe class. It is not enough to synchronize the set method only, and the call to get thread will still see the invalid value.
Public class Synchronizedinteger { privateint value; Public int get () { return value; } Public void set (int value) { this. Value = value; }}
3.1.2 64-bit operation for non-atomic
Ignore...
3.1.3 Locking and visibility
Built-in locks can be used to ensure that a thread can see the execution results of another thread in a predictable way. For the same lock, the thread that is behind the lock can see all the results of the operation of the front-end in the lock (the lock guarantees visibility).
The meaning of locking is not limited to mutex behavior, but also includes memory visibility . To ensure that all threads are able to see the latest values of shared variables, all threads that perform read or write operations must synchronize on the same lock
3.1.4 Volatile variable
For a detailed description of the volatile keyword, we recommend that you carefully watch the volatile keyword parsing, so in this do not introduce.
3.2 Release and escape
" Publish (Publish)" an object that is the object can be used in code outside the current scope. For example, save a reference to the object to a place where other code can access it, or return the reference in a non-private method, or pass the reference to a method in another class. In many cases, we want to make sure that the object and its internal state are not published. In some cases, we need to publish an object, but if you want to ensure thread safety at the time of publication, you may need to synchronize. This is known as escaping (escape)when an object that should not be published is published.
The simplest way to publish an object is to save the object's reference to a public static variable so that any class and thread can see the object, as follows. Publish an Object
Public class knownsecrets { publicstatic set<secret> knownsecrets; Public void Initialize () { new hashset<secret>(); }}
Program manifest: Is the internal mutable state escaping:
Public class unsafestates { privatenew string[] {"AK", "AL"...}; Public string[] GetStates () { return states; }}
There is a problem with how to publish states as described above, because any caller can modify the contents of this array. In this instance, the array states has escaped its scope because the variable that was supposed to be private has been released.
When an object is published, all objects referenced in the object's non-private domain are also published. In general, if an already published object can reach other objects through non-private variable references and method calls, those objects will also be published.
3.3 Thread closure
Synchronization is often required when accessing shared mutable data. One way to avoid using synchronization is to enjoy data differently. If data is accessed only in single-wire range, no synchronization is required. This technique, called thread confinement, is one of the simplest ways to implement thread safety.
A common application of thread closure techniques when JDBC is a Connection object. The thread obtains a Connection object from the connection pool, and uses that object to process the request, and then returns the object to the connection pool when it is finished. Because most requests are handled synchronously by a single thread, and the connection pool no longer assigns it to other threads until the Connection object returns, this connection management mode implicitly encloses the Connection object in the thread when processing the request.
3.3.1 Ad-hoc Thread closure
Slightly...
3.3.2 Stack Closure
A special case of a stack closed thread closure in which objects can be accessed only through local variables in a stack closure. One of the intrinsic properties of a local variable is that it is enclosed in the execution thread. They are in the execution thread's stack, and other threads cannot access the stack. Stack closure (also known as thread internal use or thread-local use, not to be confused with ThreadLocal in the Core class library).
For basic types of local variables, the numpairs of the Loadtheark method in the following list of programs does not break the stack seal anyway, because no method can get a reference to the base type , so the Java This semantics of language ensures that the underlying local variables are always closed within the thread.
Public intLoadtheark (collection<animal>candidates) {SortedSet<Aniaml>animals; intNumpairs = 0;//basic types of local variablesAniaml candidate =NULL; //animals are enclosed in methods, do not let them escape.Animals =NewTreeset<animal> (Newspeciesgendercomparator ()); Animals.addall (candidates); for(Animal a:animals) {numpairs++; } returnNumpairs;}
3.3.3 ThreadLocal Class
A more canonical way to maintain thread closeness is to use ThreadLocal, a class that associates a value in a thread with the object that holds the value. ThreadLocal provides access interfaces or methods such as get and set, which have a separate copy of each thread that uses the variable, so get always returns the most recent value set by the current execution thread when it calls set .
ThreadLocal objects are typically used to place a variable single-instance variable (Singleton) or global variable for sharing. For example, in a single-threaded application, you might maintain a global database connection and initialize the Connection object at program startup to avoid passing a Connection object when each method is called.
private static threadlocal< connection> connectionthreadlocal = new threadlocal<> () {@Override protected Object InitialValue () { return Dri Vermanager.getconnection (URL); }} public static Connection getconnection () { return Connectionthreadlocal.get (); }
When a thread first calls the Threadlocal.get method, it calls InitialValue to get the initial value. Conceptually, you can think of threadlocal<t> as containing map<thread, t> objects, which hold values specific to that thread, but ThreadLocal implementations are not. These thread-specific values are saved in the thread object, and when the thread terminates, the values are garbage collected .
3.4 invariance
An immutable object is used when another method satisfies the synchronization requirements. So far, we've covered many issues related to atomicity and visibility, such as getting stale data, losing update operations, or observing that an object is in an inconsistent state, and so on, all related to multithreaded attempts to access the same mutable state at the same time. If the state of the object does not change, then these problems and complexity will naturally disappear.
Immutable objects must be thread-safe.
Although there is no formal definition of immutability in the Java language specification and in 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:
- After an object is created, its state cannot be modified.
- All fields of an object are final types.
- object is created correctly (the This reference does not escape during the creation of the object).
See an example: Immutable classes built on mutable objects
Public class threestooges { privatefinalnew hashset<>(); Public threestooges () { stooges.add ("one"); Stooges.add ("both"); Stooges.add ("three"); } Public Boolean Isstooge (String name) { return stooges.contains (name);} }
3.4.1 Final Domain
In the Java memory model, the final domain also has a special semantics. The final domain ensures the security of the initialization process, which allows unrestricted access to immutable objects and eliminates the need for synchronization when sharing those objects.
Just as "unless you need more visibility, you should declare all the domains as private domains" is a good programming habit, "It is a good programming practice to declare a domain as final unless you need to make it mutable."
3.4.2 Example: Using volatile types to publish immutable objects
For a detailed description of the volatile keyword, we recommend that you carefully watch the volatile keyword parsing, so do not introduce too much in this. Paste a code:
Public classVolatilecachedfactorizerImplementsServlet {Private volatileOnevaluecache cache =NewOnevaluecache (NULL,NULL); Public voidService (ServletRequest request, servletresponse response) {BigInteger I=extractfromrequest (Request); biginteger[] Factors=cache.getfactors (i); if(Factors = =NULL) {Factors=factor (i); Cache=NewOnevaluecache (i, factors); } encodeintoresponse (response, factors); }}
3.5 Security release
So far, what I've focused on is how to make sure that objects aren't being published, such as enclosing an object inside a thread or another object. Of course, in some cases we want to share objects between multiple threads, and we must ensure that they are shared securely.
As follows: Publish the object without sufficient synchronization (do not do so)
// Unsafe Publishing Public Holder Holder; Public void Initialize () { new Holder;}
Because of visibility issues, Holder objects that other threads see will be in an inconsistent state, even though the inconvenience condition has been correctly constructed in the object's build function. This incorrect publication causes other threads to see objects that have not yet been created.
3.5.1 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.
As follows: This class may fail because it has not been published correctly
public class Holder { private int n; public Holder (int n" { this . N = n; public void Assertsanity () { if (n! = N) //this Sentence not understand, even if the synchronization will appear when n is likely to become a failure value, but is not (n! = N) is not an atomic operation? Solving. throw new assertionerror ("This STA Tement is false "); }}
3.5.2 immutable objects and initialization security
Because immutable objects are a very important object, the Java memory model provides a special type of initialization security for the sharing of immutable objects.
Any thread can safely access immutable objects without requiring additional synchronization, even if they are published without using synchronization.
Common patterns for 3.5.3 security releases
To safely publish an object, the reference to 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 published in the following ways:
- Initializes an object reference in the static initialization function.
- Save the object's reference 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.
The container classes in the Thread Safety Library provide a secure release guarantee:
- By placing a key or value in Hashtable, Synchronizedmap , or concurrentmap , It can be safely published to any thread that accesses it from these same periods (whether accessed directly or through an iterator)
- By placing an element in a Vector, Copyionwritearraylist, Copyonwritearrayset, Synchronizedlist , or synchronizedset , This element can be safely published to any thread that accesses the element from those 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.
In general, the simplest and safest way to publish a statically constructed object is to use a static initializer:
Public Static New Holder (42);
3.5.4 Facts Non-mutable objects
If objects are not modified after they are published, the program only needs to treat them as immutable objects.
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.
3.5.5 mutable objects
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.
The publication of an object depends on its variability:
- Immutable objects can be published by any mechanism
- Fact-immutable objects 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.
3.4.5 Sharing objects securely
When you publish an object, you must explicitly describe how the object is accessed.
When you use and share objects in concurrent programs, you can use some useful strategies that include:
thread closure : A thread-closed object can only be owned by one thread, and the object is enclosed in that thread and can only be modified by this thread.
read-only sharing : In the absence of additional synchronization, a shared read-only object can be accessed concurrently by multiple threads, but cannot be modified by any thread. Shared read-only objects include immutable objects and fact-invariant objects.
thread -safe sharing: Thread-safe objects are synchronized internally, so that threads can be accessed through the public interface of an object without further synchronization.
protected Object : The protected object can only be accessed by holding a specific lock. Protection objects include objects encapsulated in other thread-safe objects, and objects that have been published and protected by a particular lock.
"Java concurrency. 3" Object sharing