Threads will share process-wide resources, such as memory handles and file handles, but Each thread has its own program counter (Program Counter), stack, local variables, etc.
Multiple threads in the same program can also be scheduled to run on multiple CPUs at the same time.
The main synchronization mechanism in Java is the keyword synchronized, which Provides an exclusive locking method, but the term "synchronization" also includes volatile type variables, explicit locks (Explicit Lock) and atomic variables.
If proper synchronization is not used when multiple threads access the same mutable state variable, the program will error. There are three ways to fix this problem:
# Do not share state variables between threads.
Modify the state variable to an immutable variable.
Use synchronization when accessing state variables.
Thread safety definition: When multiple threads access a class, this class can always show correct behavior, then this class is said to be thread safe .
Stateless objects must be thread-safe.
The essence of most race conditions: making a judgment or performing a calculation based on a potentially invalid observation. This type of race condition is called "check before executing": a condition is first observed to be true (for example, file X does not exist), and then an appropriate action is taken based on this observation (create file Between the time you observe this result and the time you start creating the file, the observation may become invalid (another thread created file damage, etc.).
Suppose there are two operations A and B. From the perspective of the thread executing A, when another thread executes B, either all B will be executed or B will not be executed at all. , then A and B are atomic to each other. An atomic operation means that for all operations that access the same state (including the operation itself), this operation is performed atomically.
In practical situations, existing thread-safe objects (such as AtomicLong) should be used as much as possible to manage the state of the class. Compared with non-thread-safe objects, it is easier to determine the possible states of thread-safe objects and their state transitions, making it easier to maintain and verify thread safety.
To maintain state consistency, all relevant state variables need to be updated in a single atomic operation.
One way to implement reentrancy is to associate a get count and an owner thread with each lock. When the count value is 0, the lock is considered not to be held by any thread. When a thread requests a lock that is not held, the JVM will record the lock holder and set the acquisition count to 1. If the same thread acquires the lock again, the counter will be incremented, and when the thread exits the synchronized block, the counter will be decremented accordingly. When the count reaches 0, the lock will be released.
Not all data needs to be protected by locks. Only variable data that is accessed by multiple threads at the same time needs to be protected by locks.
Be sure not to hold locks when performing long calculations or operations that may not complete quickly (for example, network IO or console IO).
, I think it is a member variable of the class. A stateless object means that member variables cannot store data, or they can store data but the data is immutable. Stateless objects are thread-safe. If there is a member variable in the method, relevant thread-safe operations need to be performed on this member variable.
Don't blindly add synchronized before the method. This can ensure thread safety, but the concurrency function of the method will be weakened, causing the method that could originally support concurrency to become blocked, resulting in program processing speed slow down.
The code surrounded by synchronized should be as short as possible, but all affected member variables should be kept together. Unrelated member variables can be surrounded by multiple synchronized.
The meaning of locking is not limited to mutual exclusion. Also includes memory visibility. To ensure that all threads see the latest value of a shared variable, all threads performing read or write operations must be synchronized on the same lock.
The Java language provides a slightly weaker synchronization mechanism, namely volatile variables, to ensure that other threads are notified of variable update operations. When a variable is declared as volatile, the compiler and runtime will notice that the variable is shared, and therefore operations on the variable will not be reordered with other memory operations. Volatile variables are not cached in registers or somewhere invisible to other processors, so reading a variable of volatile type always returns the most recently written value.
No locking operation is performed when accessing volatile variables, so the execution thread will not be blocked. Therefore, volatile variables are a more lightweight synchronization than the synchronized keyword. mechanism.
volatile variables are usually used to mark the completion of an operation, an interruption, or a status. The semantics of volatile are not sufficient to ensure atomicity of the increment operation (count) unless you can ensure that only one thread performs the write operation to the variable.
The locking mechanism can ensure both visibility and atomicity, while volatile variables can only ensure visibility.
Volatile variables should be used if and only if all of the following conditions are met:
Writing operations on variables are not allowed Depend on the current value of the variable, or you can ensure that only a single thread updates the variable's value.
This variable will not be included in the invariance condition along with other state variables.
No need to lock when accessing this variable.
"Publishing" an object means making the object available for use in code outside the current scope.
When an object is published that should not be published, this situation is called an escape (Escape).
Do not let this lead escape during the construction process.
If you want to register an event listener or start a thread in the constructor, you can use a private constructor and a public factory method to avoid errors construction process.
Stack closure is a special case of thread closure. In stack closure, objects can only be accessed through local variables.
A more standardized way to maintain thread closure is to use ThreadLocal. This class can associate a value in the thread with the object that holds the value.
ThreadLocal objects are usually used to prevent mutable single instance objects (Singleton) or global variables from being shared.
An object is immutable when the following conditions are met:
The state of an object cannot be modified after it is created.
All fields of the object are final types.
The object is created correctly (the this reference did not escape during the creation of the object).
#Immutable objects must be thread-safe.
To safely publish an object, the object's reference and the object's state must be visible to other threads at the same time. A properly constructed object can be safely released by:
#Initializing an object reference in a static initialization function.
Save the object reference to a volatile type field or an AtomicReferance object.
Save the reference of the object to the final type field of a correctly constructed object.
Save a reference to the object in a field protected by a lock.
#A de facto immutable object that is safely published can be safely used by any thread without additional synchronization.
The publishing requirements of an object depend on its mutability:
Immutable objects can be published by any mechanism.
The fact is that 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.
There are some practical strategies you can use when using and sharing objects in concurrent programs, including:
Threads closed. A thread-enclosed object can only be owned by one thread, the object is enclosed in that thread, and can only be modified by this thread.
Read-only sharing. Without additional synchronization, a shared read-only object can be accessed concurrently by multiple threads, but no thread can modify it. Shared read-only objects include immutable objects and de facto immutable objects.
Thread-safe sharing. Thread-safe objects are synchronized internally, so multiple threads can access them through the object's public interface without further synchronization.
Protected object. Protected objects can only be accessed by holding a specific lock. Protected objects include objects that are encapsulated in other thread-safe objects, as well as objects that are released and protected by a specific lock.
Understanding of release and escape: that is, member variables or objects in a class can be referenced and used by other classes It is released, such as a static variable modified with static or the object of the current calling method. Escape refers to the problem that the member variable or object is exposed and referenced when it should not be referenced by multiple threads, causing its value to be incorrectly modified. In a word, don't expand the scope of a class and internally used member variables and methods. This is also an issue that packaging should consider.
This escape: That is, another thread is started in the inner class of the constructor to reference this object, but at this time the object has not been constructed yet, which may lead to unexpected mistake. The solution is to create a factory method and then make the constructor private.
The member variable modified by final needs to be initialized in the constructor, otherwise the member variable cannot be assigned a value after the object is instantiated. When the member variable modified by final refers to an object, the address of the object cannot be modified, but the value of the object can be modified.
Understanding of the four ways to safely publish an object, for example, there is a reference to class B in class A:
A's Static initialization method, such as public static A a = new A(b); In such a static factory class, B is initialized when B is referenced.
The B member variable in class A is modified with volatile b or AtomicReferance b.
#The B member variable in class A is modified like this with final B b.
When the method in class A uses B, it is surrounded by synchronized(lock){B...}.
#The simple understanding of immutable objects is that they are technically mutable, but they will not be modified during business logic processing.
Related recommendations:
Java thread-wide and shared resources
java thread safety and immutability
The above is the detailed content of Summary of JAVA concurrent programming: thread safety, object sharing. For more information, please follow other related articles on the PHP Chinese website!