Hello, little friends hello, I am IT brother \
Let’s take a real look at how Synchronized implements locking at the JVM level
Without further ado, get right to the code
This is a Java class that uses Synchronized in several ways:
public class SyncTest {
public synchronized void methodOne(a) {}public void methodTwo(a) {
synchronized (this) {}}public synchronized static void methodThree(a) {}}Copy the code
Compile the SyncTest above into a class file
Javac SyncTest. Java — — > SyncTest. Class
Class files can’t be viewed directly, they have to be compiled into bytecode files
We pass this command: \
javap -v SyncTest.class\
If you are unfamiliar with javap command, go to \
This is the compiled bytecode file: \
{
public com.bilibili.itlaoge.juc.SyncTest();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."
":()V
4: return
LineNumberTable:
line 3:0
public synchronized void methodOne();
descriptor: ()V
flags:ACC_PUBLIC, ACC_SYNCHRONIZED // Key of synchronization lock
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 7:0
public void methodTwo();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: aload_0
1: dup
2: astore_1
3: Monitorenter // Enters the lock
4: aload_1
5: Monitorexit // Releases locks
6: goto 14
9: astore_2
10: aload_1
11:Monitorexit // Ensures that locks are released properly in the event of an exception
12: aload_2
13: athrow
14: return
Exception table:
from to target type
4 6 9 any
9 12 9 any
LineNumberTable:
line 10:0
line 11:4
line 12:14
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 9
locals = [ class com/bilibili/itlaoge/juc/SyncTest, class java/lang/Object ]
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 4
public static synchronized void methodThree();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED //
Code:
stack=0, locals=0, args_size=0
0: return
LineNumberTable:
line 16:0
}
Copy the code
Ps: As a result of the decompile, the method is not synchronized through monitorenter and Monitorexit (theoretically, it could be), but has the ACC_SYNCHRONIZED flag in its constant pool compared to the normal method. The JVM synchronizes methods based on this identifier: When a method is invoked, the calling instruction checks whether the ACC_SYNCHRONIZED access flag of the method is set. If so, the thread of execution obtains monitor, executes the method body after the method is successfully acquired, and releases Monitor after the method is executed. During method execution, the same Monitor object is no longer available to any other thread. There is essentially no difference, except that method synchronization is done implicitly, without bytecode.
A description of the MONITorenter from the JVM is as follows:
Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, asFollows: • If the entry countof the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of• If the thread already owns the monitor associatedwithobjectref, it reenters the monitor, • Increits entry count. • If another thread already owns the monitor associatedwith objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership.
Copy the code
It means:
Each object has a monitor lock. The monitor is locked when it is occupied, and the thread attempts to acquire ownership of the Monitor when it executes the Monitorenter instruction as follows:
1. If the number of entries to monitor is 0, the thread enters monitor and sets the number of entries to 1. The thread is the owner of Monitor.
2. If the thread already owns the monitor and just re-enters, the number of entries into the monitor is increased by 1.
3. If the monitor is occupied by another thread, the thread blocks until the number of monitor entries is 0, and then tries again to acquire ownership of the monitor.
Monitorexit:
The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.
The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.
Copy the code
The passage roughly means:
The thread executing monitorexit must be the owner of the monitor to which objectref corresponds.
When the instruction is executed, the number of monitor entries decreases by 1. If the number of monitor entries decreases by 1, the thread exits the monitor and is no longer the owner of the monitor. Other threads blocked by the monitor can try to take ownership of the monitor.
Synchronized semantics are implemented through a monitor object. In fact, wait/notify and other methods also rely on monitor objects. This is why only in the synchronized block or method calls to wait/notify method, otherwise will be thrown. Java lang. The cause of the exception IllegalMonitorStateException.
A general understanding of the principle of synchronized, below give you a few questions to play
** We judge that there is no lock competition in these cases **
** First question: **
public class SynchronizedTest {
public synchronized void methodOne(){
System.out.println("Method One start");
try {
System.out.println("Method One execute");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method One end");
}
public synchronized void methodTwo(){
System.out.println("Method Two start");
try {
System.out.println("Method Two execute");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method Two end");
}
public static void main(String[] args) {
final SynchronizedTest test = new SynchronizedTest();
new Thread(new Runnable() {
@Override
public void run() {
test.methodOne();
}
}).start();
new Thread(new Runnable() {
@Override
public void run(){ test.methodTwo(); } }).start(); }}Copy the code
Question 2: \
public class SynchronizedTest2 {
public static synchronized void methodOne(){
System.out.println("Method One start");
try {
System.out.println("Method One execute");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method One end");
}
public static synchronized void methodTwo(){
System.out.println("Method Two start");
try {
System.out.println("Method Two execute");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method Two end");
}
public static void main(String[] args) {
final SynchronizedTest test = new SynchronizedTest();
final SynchronizedTest testTwo = new SynchronizedTest();
new Thread(new Runnable() {
@Override
public void run() {
test.methodOne();
}
}).start();
new Thread(new Runnable() {
@Override
public void run(){ testTwo.methodTwo(); } }).start(); }}Copy the code
Item 3: \
public class SynchronizedTest3 {
public void methodOne(){
System.out.println("Method One start");
try {
synchronized (this) {
System.out.println("Method One execute");
Thread.sleep(2000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method One end");
}
public void methodTwo(){
System.out.println("Method Two start");
try {
synchronized (this) {
System.out.println("Method Two execute");
Thread.sleep(2000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method Two end");
}
public static void main(String[] args) {
final SynchronizedTest test = new SynchronizedTest();
new Thread(new Runnable() {
@Override
public void run() {
test.methodOne();
}
}).start();
new Thread(new Runnable() {
@Override
public void run(){ test.methodTwo(); } }).start(); }}Copy the code
You should answer carefully, haha
Give a [look], is the biggest support for IT elder brotherCopy the code