Synchronized is introduced:
In concurrent programming, multi-threaded concurrent access to resources is called critical resources at the same time, when multiple threads access object at the same time and requires the same resource, splitting the atom operations there is possible data inconsistent or incomplete data, in order to avoid the happening of this kind of situation, we will take the synchronization mechanism, to ensure that at some point, method allows only one thread.
A Java keyword used to lock objects and methods or blocks of code. When it locks a method or block of code, at most one thread executes the code at a time. Each instance of a class has its own object-level lock. When a thread accesses a synchronized block or method in an instance object, that thread acquires the object level lock of the instance. Other threads accessing a synchronized block or method must block and wait until the preceding thread exits. The object level lock is released.
A synchronization mechanism that uses the synchronized modifier is called a mutex mechanism, and the lock it acquires is called a mutex. Each object has a Monitor (lock marker), which can be accessed only if the thread owns the lock marker. Without the lock marker, the thread enters the lock pool. Any object system creates a mutex for it, which is assigned to threads to prevent breaking atomic operations. The lock on each object can only be assigned to one thread and is therefore called a mutex.
Here are a few notes on the use of synchronization to obtain mutex:
1. If there are two or more threads in the same method, each thread has its own copy of local variables.
2. Each instance of a class has its own object-level lock. When a thread accesses a synchronized block or method in an instance object, that thread acquires the object level lock of the instance. Other threads accessing a synchronized block or method must block and wait until the preceding thread exits. The object level lock is released.
3. Access to synchronized code blocks in different instances of the same class, there is no problem of blocking waiting to acquire the object lock, because they acquire the object level lock of their own instances and have no effect on each other.
4. Holding an object-level lock does not prevent that thread from being swapped out, nor does it block other threads from accessing non-synchronized code in the same sample object. When A thread A holds an object-level lock (that is, in A synchronized modified block or method), the thread can also be swapped out, and thread B may acquire the time to execute the code in that object, but it can only execute unsynchronized code (without the synchronized modification). When it executes synchronized code, Perhaps the thread planner lets thread A run again, and thread A continues to hold the object-level lock. When thread A exits the synchronized code (that is, releasing the object-level lock), if thread B runs again at this time, it acquires the object-level lock and executes the synchronized code.
5. A thread holding an object-level lock causes other threads to block out of all synchronized code. For example, if there are three synchronized methods a, B, and C in a class, thread A acquires the object-level lock while executing method A on an instance object M, and other threads block at all synchronized methods while executing code on the same instance object M. That is, methods A, B, and C are blocked, and when thread A releases the object-level lock, other threads can execute the code in methods A, B, or C to acquire the object-level lock.
6. Use synchronized (obj) to obtain an object-level lock on a specified object. Obj is a reference to an object. If an object level lock is obtained on an OBJ object, when an object is concurrently accessed, the synchronized code blocks and waits until the object level lock of the OBJ object is obtained. When obj is this, the object level lock for the current object is acquired.
7. Class-level locking is shared by all examples of a particular class and is used to control concurrent access to static member variables and static methods. The usage is similar to object-level locking.
8. Mutex is a means to achieve synchronization. Critical sections, mutex and semaphores are the main ways to achieve mutex. The synchronized keyword is compiled to form monitorenter and Monitorexit bytecode instructions before and after the synchronized block. According to the virtual machine specification, when monitorenter is executed, it first attempts to acquire the lock on the object. If it obtains the lock, it increments the lock counter by one. Accordingly, when Monitorexit is executed, it decrement the lock counter by one. Since synchronized blocks are reentrant to the same thread, a thread can acquire the mutex of the same object multiple times. Similarly, the mutex must be released a corresponding number of times before the lock is finally released.
Memory visibility
The power of synchronized is not limited to mutually exclusive behavior, but also has another important aspect: memory visibility. Not only do we want to prevent one thread from using object state while another thread changes it, but we also want to ensure that when one thread changes object state, other threads see the change. Synchronization of threads does exactly that. Built-in locks can be used to ensure that one thread sees the results of another thread’s execution in a predictable way. To ensure that all threads see the latest value of a shared variable, you can place the same lock on all threads that perform read or write operations
When thread A executes A block of synchronized code, thread B then enters the block of synchronized code protected by the same lock. In this case, it is guaranteed that all variable values seen by thread A before the lock is released (including variables Y and X seen by thread A before the lock is released) will be seen by thread B after the lock is acquired. In other words, when thread B executes A lock protected block of synchronized code, it can see the result of all previous operations by thread A in the same lock protected block of synchronized code. If thread B enters lock M after thread A unlock M, then thread B can see what thread A did before unlock M and get I =1, j=1. If thread A enters lock M after thread B unlock M, then thread B does not necessarily see the operation in thread A, so the value of j does not have to be 1.
Now consider the following code:
public class MutableInteger{
private int value;
public int get(){
return value;
}
public void set(int value){
this.value = value;
}
}
In the above code, both the GET and set methods access value without synchronization. If the value is shared by multiple threads, if one thread calls set, then another thread that is calling GET may or may not see the updated value.
MutableInteger can be made a thread-safe class by synchronizing the set and GET methods, as follows:
public class SynchronizedInteger{
private int value;
public synchronized int get(){
return value;
}
public synchronized void set(int value){
this.value = value;
}
}
The set and GET methods are synchronized, and the same object lock is added, so that the get method can see the change of value in the set method, so that the value obtained by the get method is the latest value.
Introduction to Volatile:
Main memory: space shared memory, in Java heap memory;
Local memory: thread memory, machine registers
Prior to JDK1.2, Java’s type model implementation always read variables from main memory and did not require special attention. As the JVM matures and optimizes, the use of volatile becomes increasingly important in multithreaded environments. Under the current Java memory model, threads can store variables in the current thread memory rather than reading or writing directly from the heap memory. This can cause one thread to change the value of a variable in the heap, while another thread continues to use a copy of its variable value in the thread, causing data inconsistency.
The Java language specification states that for optimal speed, threads are allowed to keep private copies of their member variables, which are stored in thread memory, and the previous copy of the current thread is compared to the original value in heap memory only when the thread enters or leaves a synchronized code block. When multiple threads interact with an object at the same time, care must be taken to keep the threads informed of changes in heap memory variables. To solve this problem, declare the variable volatile, which tells the JVM that the variable is volatile and that it is read into heap memory each time it is used. As a general rule, variables shared between tasks should be volatile in a multitasking environment. A volatile variable forces the value of the member variable to be re-read from shared memory each time it is accessed by a thread. Furthermore, when a member variable changes, the thread is forced to write the changed value back to heap memory. This way, at any time, two different threads will always see the same value of a member variable. Volatile is a weaker synchronization mechanism. Locking is not performed when accessing volatile variables, and therefore thread blocking is not performed. Volatilei is therefore a lighter synchronization mechanism than the volatilei keyword.
Usage suggestions:
Synchronized is safer for multiple concurrency, and synchronized is recommended to ensure thread safety in most cases. You can use volatile on member variables that two or more threads need to access. It is unnecessary to use volatile when the variable to be accessed is already in a synchronized block, is either a constant, or is a constant. Using volatile is inefficient because it blocks out necessary code optimizations in the JVM, so use this keyword only when necessary.
Reference links:
www.cnblogs.com/yshb/archiv…