Wedge
In the last article, we learned the volatile keyword. In this article, we continue to learn another keyword commonly used in concurrent programming - synchronized.
synchronized usage
First, let's look at several common ways to use synchronized.
Use synchronized(this) to decorate code blocks
class SyncThread implements Runnable { private static int count; public SyncThread() { count = 0; } public void run() { synchronized (this){ for (int i = 0; i < 50; i++) { try { System.out.println(Thread.currentThread().getName() + ":" + (count++)); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
public class Test { public static void main(String[] args) { System.out.println("The same object, using synchronized keyword"); SyncThread syncThread = new SyncThread(); Thread thread1 = new Thread(syncThread, "Same object: thread"); Thread thread2 = new Thread(syncThread, "Same object: thread 2"); thread1.start(); thread2.start(); } }
results of enforcement
Same object: thread:0 ...... Same object: thread 2:99
Use synchronized to modify common methods
We modify the SyncThread class and add the synchronized keyword to the run method
class SyncThread implements Runnable { private static int count; public SyncThread() { count = 0; } public synchronized void run() { for (int i = 0; i < 50; i++) { try { System.out.println(Thread.currentThread().getName() + ":" + (count++)); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
results of enforcement
Same object: thread:0 ...... Same object: thread 2:99
Modifying static methods with synchronized
We modify the SyncThread class, the extraction method is static, and add the synchronized keyword to the extracted method
class SyncThread implements Runnable { private static int count; public SyncThread() { count = 0; } public void run() { doSome(); } private synchronized static void doSome() { for (int i = 0; i < 50; i++) { try { System.out.println(Thread.currentThread().getName() + ":" + (count++)); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
results of enforcement
Same object: thread:0 ...... Same object: thread 2:99
Use synchronized(xxx.class) to decorate a class
class SyncThread implements Runnable { private static int count; public SyncThread() { count = 0; } public void run() { synchronized (SyncThread.class){ for (int i = 0; i < 50; i++) { try { System.out.println(Thread.currentThread().getName() + ":" + (count++)); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
results of enforcement
Same object: thread:0 ...... Same object: thread 2:99
Summary
Through the above basic use, we can boldly guess that synchronized actually locks objects, whether instance objects or class objects, why does it lock objects? In fact, the underlying principle of synchronized is related to jvm instructions and monitor.
synchronized underlying principle
Each object has a monitor. If you use the synchronized keyword, there will be two instructions monitorenter and monitorexit in the underlying compiled jvm instructions.
There is a counter starting from 0 in monitor. If a thread wants to obtain the lock of the monitor, it needs to check whether the counter of the object is 0. If it is 0, it means that no one has obtained the lock, he can obtain the lock, and then add 1 to the counter; If the counter is 1, it indicates that the current lock has been occupied by others. At this time, he will wait for a certain time before trying to obtain the lock; If the scope of synchronized modification is out, there will be a monitorexit instruction, and the monitor counter of the object will be decremented by 1. The value mentions that the monitor is reentrant, that is, an object can be locked multiple times. When the lock is added several times, the counter will increase several times, and when the lock is released, it will decrease, and finally become 0.
Here is a simple example
public class SynchronizedDemo { public static void main(String[] args) { synchronized (SynchronizedDemo.class) { } method(); } private static void method() { } }
Compile the above files and execute javap - V synchronizeddemo. In the directory corresponding to the target Class, you can see the corresponding bytecode as follows:
wait and notify
First, let's look at the following simple examples
class ThreadA extends Thread{ public ThreadA(String name) { super(name); } public void run() { synchronized (this) { try { // Block the current front line for 1 s and ensure T1 of the main program wait(); Execute before executing notify() Thread.sleep(5000); } catch (Exception e) { e.printStackTrace(); } System.out.println("Wait 5 s end "+Thread.currentThread().getName()+" Awakened"); // Wake up the current wait thread this.notify(); } } }
public class WaitTest { public static void main(String[] args) { ThreadA t1 = new ThreadA("t1"); synchronized(t1) { try { // Start "thread t1" System.out.println(Thread.currentThread().getName()+" implement t1 thread "); t1.start(); // The main thread waits for t1 to wake up via notify(). System.out.println(Thread.currentThread().getName()+" Start waiting"); // Instead of making the t1 thread wait, the thread currently executing wait waits t1.wait(); System.out.println(Thread.currentThread().getName()+" Wait for the end to continue"); } catch (InterruptedException e) { e.printStackTrace(); } } } }
The result is:
main implement t1 thread main Start waiting Wait 5 s end t1 Awakened main Wait for the end to continue
The underlying principles of wait and notify are also based on monitor. The underlying layer calls the wait and set methods respectively, so they must lock, wait and notify the same object. In this way, they can operate through the counters, wait and set related to monitor in an object.
reflection
Thinking one
Is the value of count 99 in the following example? Why?
public static void main(String[] args) throws InterruptedException { System.out.println("Different objects, using synchronized keyword"); Thread thread3 = new Thread(new SyncThread(), "Different objects: Line 3"); Thread thread4 = new Thread(new SyncThread(), "Different objects: thread four"); thread3.start(); thread4.start(); }
class SyncThread implements Runnable { private static int count; public SyncThread() { count = 0; } public void run() { synchronized (this){ for (int i = 0; i < 50; i++) { try { System.out.println(Thread.currentThread().getName() + ":" + (count++)); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } this.notify(); } } }
Thinking II
Is the value of count 99 in the following example? Why?
public static void main(String[] args) throws InterruptedException { System.out.println("Different objects, using synchronized keyword"); Thread thread3 = new Thread(new SyncThread(), "Different objects: Line 3"); Thread thread4 = new Thread(new SyncThread(), "Different objects: thread four"); thread3.start(); thread4.start(); }
class SyncThread implements Runnable { private static int count; public SyncThread() { count = 0; } public void run() { synchronized (SyncThread.class){ for (int i = 0; i < 50; i++) { try { System.out.println(Thread.currentThread().getName() + ":" + (count++)); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } }