Preliminary Research on concurrent programming-object sharing and concurrent programming
Object sharing 1. Visibility
In the absence of synchronization, the compiler and runtime may make some unexpected adjustments to the execution sequence of the operations. In the absence of a multi-threaded program with sufficient synchronization, to determine the execution sequence of memory operations, we can hardly draw a correct conclusion.
1. An error may occur in a program that lacks synchronization: Invalid Data.
2. Non-atomic operations
When the thread reads a variable without the same value, it may get an invalid value, but at least this value is set by a thread rather than a random value. This security guarantee is also called the lowest security.
The minimum security applies to the vast majority of variables, but there is an exception: non-volatile 64-bit numeric variables.
The Java memory model requires that the read and write operations on variables must be atomic, but for non-volatile long and double variables, JVM allows 64-bit read or write operations to be divided into two 32-bit operations.
When reading a non-volatile long variable, if the read and write operations on the variable are executed in different threads, I am likely to read a value worth 32 bits and another value worth 32 bits. Therefore, even if you do not consider invalid data, it is not safe to use variables such as shared variables long and double in multi-threaded programs. Unless they are declared with the keyword volatile, or locked for protection.
3. Locking and visibility
The meaning of the lock is not only limited to mutex, but also includes memory visibility. To ensure that all threads can see the latest value of the shared variable, all threads that execute read or write operations must be synchronized in the same lock.
4. volatile variable
Volatile variables are used to notify other threads of variable update operations. When the variable is declared as volatile, the compiler and runtime will notice that this variable is shared, so operations on this variable will not be reordered with other memory operation instruments.
Volatile variables are not cached in registers or invisible to other processors. Therefore, the latest written values are always returned when you read volatile variables.
When you access the volatile variable, the lock operation will be performed, so the thread will not be blocked. Therefore, the volatile variable is a more lightweight synchronization mechanism than the synchronized keyword.
Only volatile variables can simplify code implementation and verify synchronization policies. If you need to make a complex judgment on the visibility when verifying the correctness, do not use the volatile variable for a long time. The correctness of volatile variables can be used to ensure visibility of their own states, to ensure visibility of the states of the objects they reference, and to identify the occurrence of more important program lifecycle events (for example, initialization or shutdown ).
A typical usage of the volatile variable: checks the status of a tag to determine whether to exit the loop.
The locking mechanism ensures both visibility and atomicity, while volatile variables only ensure visibility.
The volatile variable is used only when all the conditions are met:
A) write operations on variables do not depend on the current value of the variable, or you can ensure that only a single thread updates the value of the variable.
B) This variable will not be included in the immutable condition together with other variables.
C) No need to lock Variables
Ii. Release and escape
Publish refers to an object that can be used in code outside the current scope.
When an object that should not be published is published, this situation becomes escaped ).
The simplest way to publish an object is to save the object reference to a public static variable so that any class or thread can see the object.
1. Security Object Construction Process
Do not escape this reference during construction.
When an object creates a thread in its constructor, it is explicitly created (by passing it to the constructor) or is it implicitly created (the Thread or Runnable is an internal class of the object), this reference will be shared by the newly created Thread. Before the object is fully constructed, the first thread can see it. There is no error in creating a thread in the constructor, but it is better not to start it immediately, but to start it through a start or initialize method. When calling an instance method that cannot be rewritten in the constructor, this reference will also escape during the constructor.
3. Closed threads
Synchronization is usually required to access Shared variable data. One way to avoid synchronization is not to share data. If you only access data within a single Thread, you do not need to synchronize data. This technology is called Thread Confinement. It is one of the simplest ways to implement Thread security. When an object is closed in a thread, this method automatically achieves thread security, even if the closed object itself is not thread-safe.
The thread blocking technology is widely used in Swing.
The thread closure technology is another common application that enables the JDBC connection object. The JDBC specification requires that the connection object be thread-safe. In a typical server application, the thread obtains a connection object from the connection pool and uses this object to process requests. After the connection pool is used, the object is returned to the connection pool. Because most requests are processed by a single thread in synchronous mode, and before the connection object returns, the connection pool will no longer allocate it to other threads, this connection management mode implicitly closes the connection object in the thread when processing the request.
The Java language does not force a variable to be locked for protection, nor does it force an object to be enclosed in a thread. A Consideration of thread closures in program design must be implemented in the program. Java language and core library provide some mechanisms to help maintain thread closures, such as local variables and ThreadLocal classes, even so, the programmer still needs to ensure that the objects in the thread do not escape from the thread.
1. Ad_hoc thread closed
Ad_hoc thread closures mean that the responsibilities for maintaining the closed thread are completely borne by the Program Implementation class. Ad_hoc thread closures are very fragile because they do not have any language features, for example, the visibility modifier or local variable can close the object to the target thread.
When we decide to use the thread closure technology, it is usually because a specific subsystem needs to be implemented as a single-threaded subsystem. In some cases, the simplicity provided by the single-thread subsystem is better than the vulnerability of the Ad_hoc thread closure technology. Another reason for using the single-threaded subsystem is to avoid deadlocks.
2. Stack closures
Stack blocking is a special case of thread blocking. In stack blocking, the inherent attribute of object local variables can only be accessed through local variables is closed in the execution thread of the program.
If a non-thread-safe object is used in the internal context of the thread, the object is still thread-safe.
3. ThreadLocal class
ThrashLocal is a more standard way to maintain thread closures. This class can associate a Value Field in a thread to save the objects worth it. ThreadLocal provides access interfaces or methods such as get and set. These methods store an independent copy for each thread that uses the variable, therefore, get always returns the latest value set when the current execution thread calls set.
ThreadLocal objects are usually used to prevent sharing of possible single-instance variables or global variables.
When a frequently executed operation requires a temporary variable operation, such as a buffer zone, and at the same time, you want to avoid updating and allocating the object during each execution, you can use this technology.
ThreadLocal variables are similar to global variables, which can reduce code reusability and introduce implicit coupling between classes. Therefore, you must be very careful when using them.
4. immutability
Another method for synchronization is to use Immutable objects ).
If the status of an object cannot be modified after it is created, the object is called an immutable object. Thread security is one of the inherent attributes of immutable objects. Their immutability conditions are created by constructors and can be maintained as long as they do not change.
Objects are unchangeable when the following conditions are met:
1) After an object is created, its status cannot be modified.
2) all fields of the object are of the final type.
3) The object is correctly created (this reference is not escaped during object creation)
Final domain: the keyword final can be regarded as a restricted version of the const Mechanism in C ++ and used to construct immutable objects. Final fields cannot be modified. (However, if the objects referenced by the final field are variable, these referenced objects can be modified ). The final domain ensures the security of the initialization process and allows unrestricted access to immutable objects without synchronization when sharing these objects. Unless higher visibility is required, all domains should be declared as private domains. Unless a domain is variable, it should be declared as a final domain.
For competition issues that occur when multiple variables are accessed and updated, you can save all these variables in an immutable object to eliminate them. If they are a mutable object, when the thread obtains the reference of this object, it does not have to worry that another thread will modify the object state. If you want to update these variables, you can create a new container, but other threads that use the original object will still see the object in the consistent state.
5. Secure Publishing
Incorrect release: The correct object is damaged. An object that has not been fully created cannot be expected to have integrity.
Immutable objects and initialization security: any thread can securely access immutable objects when additional synchronization is required, even if no synchronization is used when these objects are published.
1. common modes of secure publishing:
1) to securely publish an object, the object reference and object state must be visible to other threads at the same time. A correctly constructed object can be securely published in the following way:
A) Initialize an object reference in the static initialization function.
B) Save the object reference to a volatile domain or AtomitReference object.
C) Save the object reference to the final type domain of a correctly constructed object.
D) Save the object reference to a domain protected by the lock.
2) the containers in the thread security library provide the following security release guarantees:
A) put a key or value into Hashtable, synchronizedMap or ConscurrentMap can securely publish it to any thread accessed from these containers (whether directly or through the iterator ).
B) by placing an element in a Vector, CopyOnWriteArrayList, CopyOnWriteArraySet, SynchronizedList, or SynchronizedSet, the element can be securely published to any thread that accesses the element from these containers.
C) by placing an element in BlockingQueue or concurrentincluqueue, the element can be securely published to the thread that accesses the element from these queues.
3) a static object is usually published. The simplest and most secure way is to use a static initiator. The static initiator is executed by the JVM in the class initialization phase. Because a synchronization mechanism exists in the JVM, any object initialized in this way can be securely released.
2. Fact immutable object
If an object is technically changeable but its status does not change after it is released, such an object is called a "fact immutable object ". Without extra synchronization, any thread can safely use the fact immutable objects that have been securely released.
3. Variable object
The release requirement of an object depends on its variability:
1) immutable objects can be released through any mechanism
2) fact immutable objects must be published through secure Publishing.
3) The variable object must be released in a secure manner and must be thread-safe or protected by a lock.
4. Security sharing objects
Some practical policies can be used to use and share objects in concurrent programs, including:
1) thread closures: A thread-closed object can only be owned by one thread. The object is closed in this thread and can only be modified by this thread.
2) read-only sharing: when there is no additional day of synchronization, the shared read-only object can be accessed by multiple threads concurrently, but cannot be modified by any thread. Shared read-only objects include immutable objects and fact immutable objects.
3) thread-safe sharing: the thread-safe objects are synchronized internally. Therefore, multiple threads can access the objects through the public interfaces of the objects without further synchronization.
4) protected object: The protected object can only be accessed by holding a specific lock. Protected objects include objects encapsulated in other thread-safe objects and published objects protected by a specific lock.