"Java Concurrent Programming Practice" Study Notes Chapter 3 Object Sharing

1. Memory Visibility

    The synchronized keyword synchronization has two functions:

 (1) Implement atomicity or determine critical sections

(2) Ensure memory visibility

The so-called memory visibility means that when a thread modifies the object state, other threads can see the modified state.

     In the absence of synchronization in a multithreaded program, the compiler, processor, and runtime may all make some unexpected adjustments to the execution order of operations. In a multithreaded program that lacks sufficient synchronization, it is almost impossible to draw correct conclusions about the order in which memory operations are performed.

 

1.1 Failure data

For example, when a thread reads a variable without synchronization, it may get invalid data (a value previously set by a thread).

 

1.2 Non-atomic 64-bit operations

For non-volatile 64-bit numeric variables, such as long and double, the JVM allows a 64-bit read or write to be split into two 32-bit operations. If reads and writes to this variable are performed in different threads, it is possible to read the upper 32 bits of one value and the lower 32 bits of another value and cause an error.

 

1.3 Locking and Visibility

The meaning of locking includes not only mutual exclusion behavior, but also memory visibility. To ensure that all threads can see the latest value of a shared variable, all threads performing read or write operations must synchronize on the same lock.

 

1.4 volatile variables

A volatile variable is a slightly weaker synchronization mechanism that ensures that other threads are notified of variable updates.

Volatile variables only ensure visibility, not atomicity.

 

2. Release and Escape

Publishing an object means that the object can be used in code outside the current scope. For example, put the object in a public static variable, or return a reference to the object in a non-private method.

When an object that shouldn't be published is published, the situation is called escaping.

 

Do not escape this reference during object construction, and do not explicitly or implicitly use a reference to the current object until the object constructor completes.

 

3. Thread closure

 3.1 Ad-hoc thread closure

The responsibility of maintaining thread closure is entirely undertaken by the program implementation. For example, only a single thread performs write operations on a shared volatile variable, that is, enclosing the write operation in a single thread to prevent race conditions, and the visibility of volatile variables is also guaranteed. Other threads can see the latest value.

 

3.2 Stack closure

Even with local variables, local variables cannot be accessed outside the thread.

 

3.3 ThreadLocal类

The ThreadLocal object can associate a value in the thread with the saved object. ThreadLocal provides access interfaces or methods such as get and set, and there is an independent copy for each thread that uses the variable. The latest value set by the current thread of execution when set was called.

 

ThreadLocal objects are often used to prevent mutable single-instance variables or global variables from being shared.

 

Brief description of the implementation principle of the ThreadLocal class:

When the value of the ThreadLocal class object is initialized, a ThreadLocal.ThreadLocalMap object is created and placed in the threadLocals property of the current thread, where the key of threadLocals is the current ThreadLocal object, and the value is the value of the current ThreadLocal object.

 

 4. Immutability

 Immutable objects must be thread-safe.

 Conditions that need to be met for immutable objects:

After the object is created, its state cannot be modified;

All fields of the object are of final type;

The object is created correctly (the this reference is not escaped during the creation of the object).

 

4.1 final fields

The final field cannot be modified. In the Java memory model, final has special semantics: it can ensure the safety of the initialization process, so that immutable objects can be accessed without restriction, and there is no need to synchronize when sharing these objects.

 

4.2 Publishing immutable objects with volatile types

For race conditions when accessing and updating multiple variables, you can ensure visibility by encapsulating the variables in an immutable object and decorating the variables with volatile without worrying about other threads modifying the object's state.

 

5. Safe Release

Don't release mutable objects without sufficient synchronization.

 

5.1 Without sufficient synchronization, some very strange things will happen when data is shared among multiple threads.

 

5.2 Immutable Objects and Initialization Safety

Immutable objects can be safely accessed by any thread without additional synchronization, even if synchronization was not used when publishing those objects.

This guarantee also extends to all final fields of these objects, and fields of final types (where fields of final types implement non-mutable objects) can be safely accessed without additional synchronization.

 

 5.3 Common Modes for Safe Release

 To safely release an object, the object's reference and state must be visible to other threads at the same time. A properly constructed object can be released safely by:

(1) Initialize an object reference in a static initialization function.

(2) Save the reference of the object to the volatile type field or the AutoReference object.

(3) Save a reference to the object in a final type field that properly constructs the object.

(4) Save a reference to the object in a field protected by a lock.

 

Often the easiest and safest way to publish a statically constructed object is to use a static initializer:

public static Holder holder = new Holder (66);

Static initializers are performed by the JVM during the initialization phase of the class. Due to the synchronization mechanism within the JVM, objects initialized in this way can be released safely.

 

  5.4 Factual Immutable Objects

If objects will not be modified after release, then safe release is sufficient for other threads to safely access those objects without additional synchronization.

All safety mechanisms ensure that when a reference to an object is visible to all threads accessing the object, the state of the object at the time of publication will also be visible to all threads, and if the object state does not change again, it is sufficient to ensure that any access All are thread safe.

 

If an object is technically mutable, but its state does not change after it is published, such an object is called a "factually immutable object", and after such an object is published, the program simply treats it as immutable object.

 

  5.5 Mutable Objects

 If the object can be modified after construction, then safe publishing can only ensure the visibility of the state at the time of publishing, so for edgeable objects, not only need to synchronize when publishing, but also need to use synchronization on each access to ensure the visibility of subsequent operations sex.

 

The release requirements of an object depend on its mutability:

Immutable objects can be published by any mechanism;

factually immutable objects must be published in a safe way;

Mutable objects must be released in a safe manner, and must be thread-safe or protected by a lock.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326167452&siteId=291194637