This is the third day of my participation in Gwen Challenge
1. Use Synchronized
A mutex ensures that only one thread can execute a method or block of code (used to manipulate shared variables) at a time. Synchronized can be used to modify methods, code blocks. It is divided into the following three types:
- Decorates instance methods. To lock the current instance object, the thread needs to acquire the lock of the instance object before entering the current method. This is called the object lock.
- Decorates static methods. The lock is placed on the class object of the preceding class, and the thread needs to acquire the lock on the class object before entering the static method. This is called the class lock.
- Decorates the code block. A lock on an instance object is called an object lock. A lock on the class object of a class, called a class lock, ensures that only one thread acquires the lock at a time.
1. Modify instance methods
Instance methods modified by synchronized are nonstatic methods in which a thread acquires an object lock when it acquires one.
public class SyncTask implements Runnable {
static int count = 0;
@Override
public void run(a) {
increase();
}
private synchronized void increase(a) {
for (int i = 0; i < 1000000; i++) { count++; }}}/ / class
SyncTask syncTask =new SyncTask();
Thread thread1=new Thread(syncTask,"thread1");
Thread thread2=new Thread(syncTask,"thread2");
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" count = " + SyncTask.count);
/ / for the results
count = 2000000
Copy the code
It can be concluded that when multiple threads operate the method of the same object, only one thread can acquire the lock at the same time in the instance method modified by synchronized. After the current thread completes the execution and releases the lock, other threads can compete for the lock and execute it.
Think about it: what happens when multiple threads manipulate synchronized modified methods of different objects?
public static void main(String[] args){
Thread thread1=new Thread(new SyncTask(),"thread1");
Thread thread2=new Thread(new SyncTask(),"thread2");
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" count = " + SyncTask.count);
}
// Execution result
count = 1197724
Copy the code
The code change above is that different threads manipulate different objects. Note that SyncTask’s synchronized modified methods operate internally on the static variable count. You can see that the result is not 2 million. Therefore, it can be concluded that different threads do not affect each other when they operate synchronized modification methods of different objects. The value of the static variable count is significantly different from what was expected because there are no competing locks. An increase method that is equivalent to different threads accessing the same object without the synchronized modifier.
2. Decorate static methods
Synchronized static method that obtains the class object lock of the current class, also known as the class lock. Static modified member variables and member methods are class-specific, so concurrent operations of static methods can be guaranteed through class objects. Note that there is no conflict between thread A’s operation on an instance object’s non-static synchronized method and thread B’s operation on an instance object’s static synchronize method. There will be no mutual exclusion. Because thread A operates on object locks, thread B operates on class locks.
public class SyncTask implements Runnable {
static int count = 0;
@Override
public void run(a) {
increase();
}
private synchronized static void increase(a) {
for (int i = 0; i < 1000000; i++) { count++; }}}// Execute the function
public static void main(String[] args){
SyncTask syncTask = new SyncTask();
Thread thread1=new Thread(new SyncTask(),"thread1");
Thread thread2=new Thread(new SyncTask(),"thread2");
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" count = " + SyncTask.count);
}
// Execution result
count = 2000000
Copy the code
Synchronized modifies the static increase method and acquires a class lock, unlike instance methods, which acquire an object lock. If you operate on a class lock, only one thread acquires the lock at a time, whether you operate on different objects or the same object.
3. Embellish code blocks
If there are many methods in a method and only some parts need to be synchronized, the method of fast code synchronization can be adopted to realize partial code block synchronization. A block of synchronized code can use an object lock to hold the current object, or a class lock to hold the entire class.
public class SyncTask implements Runnable {
static int count = 0;
static SyncTask syncTask = new SyncTask();
@Override
public void run(a) {
increase();
}
private void increase(a) {
synchronized(syncTask){
for (int i = 0; i < 1000000; i++) { count++; }}}}// Execute the function
public static void main(String[] args){
SyncTask syncTask = new SyncTask();
Thread thread1=new Thread(new SyncTask(),"thread1");
Thread thread2=new Thread(new SyncTask(),"thread2");
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" count = " + SyncTask.count);
}
// Execution result
count = 2000000
Copy the code
Synchronized locks the syncTask object in increase, and each thread entering the increase method must hold the syncTask lock to continue execution, otherwise it must wait. Synchronized can also use this to indicate that the current object is locked, but as we see in the execution function each thread operates on a different object, the result is no longer the expected result. We can use class objects (class locks) to ensure that only one thread acquires the lock at a time. In a static method, you can create a static member variable or use the class object directly to ensure synchronization, since static methods can only use static member variables and the class object.
4. Differences between class locks and object locks
- Synchronized can lock an object, this keyword, or a class object when multiple threads operate on the same object. Object locks, class locks are mutually exclusive.
- Synchronized can lock a static object or a class object that adopts a class when multiple threads operate on different objects. This keyword has no mutex effect because it applies to instances of the current thread.
- Multiple threads, whether operating on the same object or multiple objects, can lock a static object or a class object. For example, the syncTask object above belongs to an object lock. The class object of the class belongs to the class lock and can be used.
2. The principle of Synchronized
The implementation of synchronized is based on Monitor. Synchronized modifies synchronized methods differently than synchronized code blocks.
2.1 Synchronized modifies the code block principle
A block of synchronized code is created in the Run method of SyncTask
public class SyncTask implements Runnable {
static int count = 0;
static SyncTask syncTask = new SyncTask();
@Override
public void run(a) {
synchronized(syncTask){
for (int i = 0; i < 1000000; i++) { count++; }}}}Copy the code
Decompilating the class file using javap-verbose class to get the bytecode, only the bytecode of the run method is captured:
public void run(a);
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=4, args_size=1
0: getstatic #2 // Field syncTask:Lcom/mdy/lib/SyncTask;
3: dup
4: astore_1
5: monitorenter // Enter the synchronization method here
6: iconst_0
7: istore_2
8: iload_2
9: ldc #3 // int 1000000
11: if_icmpge 28
14: getstatic #4 // Field count:I
17: iconst_1
18: iadd
19: putstatic #4 // Field count:I
22: iinc 2.1
25: goto 8
28: aload_1
29: monitorexit // Exit the synchronization method here
30: goto 38
33: astore_3
34: aload_1
35: monitorexit // Exit the synchronization method here
36: aload_3
37: athrow
38: return
Copy the code
You can see this implemented using Monitorenter and MonitoreXit in the synchronized code block. Monitorenter points to the start of the synchronized code block and Monitorexit points to the end of the synchronized code block. When monitorenter arrives, the current thread attempts to acquire the objectref’s monitor. When the objectref’s monitor’s entry counter is 0, the current thread acquires the monitor, and the current thread acquires the lock. If the current thread already holds monitor, the monitor is re-entered with a counter value of +1. If another thread holds objectref’s monitor, the current thread is blocked. Until the other thread completes execution, when the Monitorexit directive is executed and the entry counter value is 0, the other thread will have the opportunity to hold the lock.
The understanding of the wait
ObjectMonitor() { ... _WaitSet = NULL; _EntryList = NULL; // Set of threads in the waiting block state... }Copy the code
- When a thread requests a lock, it enters the _EntryList collection to wait and compete for the lock.
- When a thread acquires a lock, _owner marks the thread that acquired the lock
- The thread that acquired the lock is calling
wait
Method, it releases the lock and enters _WaitSet, waiting to be woken up. - When the thread in _WaitSet is awakened, it re-enters the _EntryList collection to compete for the lock.
2.2 Principle of Synchronized modification method
SyncTask uses synchronized to modify synchronization methods
public class SyncTask implements Runnable {
static int count = 0;
@Override
public void run(a) {
inCrease();
}
public synchronized void inCrease(a){
for (int i = 0; i < 1000000; i++) { count++; }}}Copy the code
Decompilating the class file using javap-verbose class to get the bytecode, only the bytecode of the inCrease method is captured:
public synchronized void inCrease(a);
descriptor: ()V
flags: (0x0021) ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=2, args_size=1
0: iconst_0
1: istore_1
2: iload_1
3: ldc #3 // int 1000000
5: if_icmpge 22
8: getstatic #4 // Field count:I
11: iconst_1
12: iadd
13: putstatic #4 // Field count:I
16: iinc 1.1
19: goto 2
22: return
Copy the code
In the decomcompiled bytecode, the synchronized synchronized method does not contain monitorenter and Monitorexit directives. ACC_SYNCHRONIZED is used to identify whether the method is a synchronized method.
2.3 Optimization of Synchronized
Before Java6, synchronized was a heavyweight lock. After Java6, biased locking and lightweight locking were added to reduce the performance cost of acquiring and releasing locks.