As mentioned in the previous article, synchronized guarantees visibility and atomicity between multiple threads. In addition, synchronized is often directly added to the method when we usually use it. This article mainly introduces some details and matters needing attention in the use of synchronized or in the implementation of synchronized
What to use as a lock
When using synchronized, it is important to know exactly what the lock is currently being claimed by each thread, so that when controlling contention, we know the range in which the thread will compete for the lock. In fact, there are only two resources that can be used as locks, one is an object, one is a Class. However, the two lock resources are divided into different cases.
Use class instance objects as locks
Synchronized (this){}, synchronized(this){}, synchronized(this){}, synchronized(this){}, synchronized(this){}, synchronized(this){}, synchronized(this){}, synchronized(this){}, synchronized(this){}, synchronized(this){}, synchronized(this){} This is not a problem if it is a singleton, but if there are multiple objects in the current class, be aware that different objects may compete for different locks.
package cn.yarne;
import java.util.concurrent.*;
/** * Created by yarne on 2021/9/12. */
public class Main {
public static void main(String[] args) throws InterruptedException {
Main main = new Main();
new Thread(main::add).start();
new Thread(main::reduce).start();
/* Main main = new Main(); Main main2 = new Main(); new Thread(main::add).start(); new Thread(main2::reduce).start(); * /
}
public synchronized void add(a) {
System.out.println("Add");
try {
TimeUnit.SECONDS.sleep(3);
} catch(InterruptedException e) { e.printStackTrace(); }}public synchronized void reduce(a) {
System.out.println("Reduce"); }}Copy the code
Use class objects as locks
Class objects can be locked on static methods using the current class’s class, or synchronized blocks using the. Class of a class. Using a Class lock ensures that no matter how many objects are created, the only resource that can be grabbed is the specified Class, because no matter how many objects are created, all classes end up with only one Class.
package cn.yarne;
import java.util.concurrent.*;
/** * Created by yarne on 2021/9/12. */
public class Main {
public static void main(String[] args) throws InterruptedException {
new Thread(Main::add).start();
new Thread(Main::reduce).start();
}
public static synchronized void add(a) {
System.out.println("Add");
try {
TimeUnit.SECONDS.sleep(3);
} catch(InterruptedException e) { e.printStackTrace(); }}public static synchronized void reduce(a) {
System.out.println("Reduce"); }}Copy the code
The four states of synchronized
It’s worth mentioning here that synchronized was highly deprecated in the original Java release because it was a heavyweight lock and had poor performance, but synchronization was optimized after Java1.6. What is the heavyweight lock in the end, how to do the optimization, the following is a brief introduction.
Lock escalation
Before 1.6, synchronized was directly a heavyweight lock without any state. However, in the later optimization, four lock states were added to it for optimization, from light to heavy, namely, no lock, biased lock, lightweight lock and heavyweight lock. Through a lock upgrade process, threads could compete for locks. Try to obtain a lock at the lowest cost to improve the overall performance. In the process of understanding the lock upgrade, you need to understand the other two knowledge, one is the object header, the other is the Monitor.
Object head
When an object is stored in memory, its layout in memory can be divided into four parts: Mark Word, class Pointer, instance, and padding-all of which we use most often are instances of an object. Class Pointer is the type pointer of an object. Represents what type the current object is. The padding is just a bit of padding. For example, if you’re dealing with a variable of a short data type, it’s usually converted to an int by a bit of padding, and if it’s not a multiple of 8, it’s a multiple of 8 by a bit of padding. Mark Word stores three types of information, including the Hash value of the object, GC information, and lock information. The information in mark Word is generally called the object head, which stores four lock states. Synchronized is the process of upgrading by manipulating the lock information in the object head.
monitor
Monitorenter, Monitorexit, monitorenter,monitorexit, monitorenter, Monitorexit, monitorenter, MonitoreXit, Monitorenter, MonitoreXit, Monitorenter, MonitoreXit The object header is used to record the status of the object being used as a lock. The object header is used to record the status of the object being used as a lock by each thread. In any case, only one thread is allowed to acquire the lock. When a thread executes a Monitorenter instruction, it retrieves a Monitor object from the object header. Let me show you some information from the Monitor object that we use to compare common operations
_recursions
: +1 each time a thread enters a synchronized block and -1 each time it exits. When the final value is 0, the lock occupation is over_object
: stores the object header information corresponding to the monitor, that is, it is used to bind the monitor to the object header_owner
: Identifies the thread to which the current monitor belongs_WaitSet
: called if the thread is occupying the lock resourceWait method for Object
, the current thread will be logged_WaitSet
Waiting to be awakened_cxq
: Information about all threads competing for locks will be recorded here_EntryList
: If a thread fails to lock and enters the blocking state of a heavyweight lock, it will be stored in this lock
Each monitor records the competition or use of the current lock by all threads
The upgrade process
In the case of multiple threads, the lock is competing with each other and the status of the competition is recorded
unlocked
First, the object header is left in an unlocked state by default, because threads have not yet contended
Biased locking
When the first thread to get the lock, and will give object hit a biased locking head of state, and record their own thread ID in object, if next time to pick the lock or the thread, and see now object is a biased locking head of state, judge stored directly towards the thread ID is himself, if is your own, There is no need to process to the next state, you can directly get the lock, or even understand that the lock is its own, you can directly manipulate the locked resource.
If the next thread to competition, found to be biased locking, and thread ID is not himself, is to judge whether the thread of the corresponding thread ID still live, and if it is alive, and also in the internal code, perform lock can’t lock is released, it will lock escalation to the next state, if the corresponding thread does not exist or not use locking, it sets the lock object to unlocked state, Reroute the bias lock process.
Lightweight lock
If lock contention begins, the first thing that happens is to compete for lock resources in a lightweight (CAS) manner. The following situations occur in this case
- If the object header is unlocked, the thread first retrieves a thread-local Monitor object from the object header, sets the _owner thread information of monitor to its own information, and passes
CAS
Try to update the owner information in your local monitor to the public monitor in the object header. The lock is acquired when the change is successful - If the current thread holds the lock and encounters the next update, it will change _RECURsions in its monitor
- If a thread is already occupied, proceed with an operation similar to step 1
CAS
If the lock is still missing after several spins, the current thread enters heavyweight contention and enters the public Monitor of the object’s header_EntryList
Block, waiting to be woken up.
Heavyweight lock
If the lock goes to a heavyweight state and a thread is blocked in the _EntryList, then all other incoming threads, if they find the _EntryList has one, will go in and wait to be woken up.
The release of
Once the lock is acquired and the locked block is executed, the Monitorexit directive is executed as it executes. The first is to contact the binding of the object header with its monitor, and the second is to wake up a thread that is blocking in the _EntryList to compete for the lock.
conclusion
This article mainly introduces some more detailed knowledge of synchronized, through which we can understand or understand the degree of optimization of synchronized, so that we can have a cognition of the lock, when using the lock, you can have a measure. There are some things I haven’t talked about yet. I will also go to learn more about this more detailed knowledge point and share it. In the next article, I will do a comprehensive combing of the knowledge of this part of lock
Reference article:
www.jianshu.com/p/19f861ab7…