Basic use of Synchronized
Synchronized is the most commonly used and simplest method to solve concurrency problems in Java.
Synchronized has three main functions:
- Ensure that threads are mutually exclusive access synchronization code
- Ensure that the modification of shared variables can be seen in time
- Effectively solve the reordering problem
Grammatically, Synchronized has three uses:
- Common methods of modification
- Modified static method
- Decorated code block
Next, I will use several example programs to illustrate the three usage methods (for comparison, the three pieces of code are basically consistent except for the different usage methods of Synchronized).
No synchronization
Code snippet 1:
public class SynchronizedTest { public void method1(){ System.out.println("Method 1 start"); try { System.out.println("Method 1 execute"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 1 end"); } public void method2(){ System.out.println("Method 2 start"); try { System.out.println("Method 2 execute"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 2 end"); } public static void main(String[] args) { final SynchronizedTest test = new SynchronizedTest(); new Thread(new Runnable() { @Override public void run() { test.method1(); } }).start(); new Thread(new Runnable() { @Override public void run() { test.method2(); } }).start(); } }
The execution results are as follows. Thread 1 and thread 2 enter the execution state at the same time. Thread 2 executes faster than thread 1, so thread 2 executes first. In this process, thread 1 and thread 2 execute at the same time.
Method 1 start Method 1 execute Method 2 start Method 2 execute Method 2 end Method 1 end
Synchronize with common methods
Code segment 2:
public class SynchronizedTest { public synchronized void method1(){ System.out.println("Method 1 start"); try { System.out.println("Method 1 execute"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 1 end"); } public synchronized void method2(){ System.out.println("Method 2 start"); try { System.out.println("Method 2 execute"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 2 end"); } public static void main(String[] args) { final SynchronizedTest test = new SynchronizedTest(); new Thread(new Runnable() { @Override public void run() { test.method1(); } }).start(); new Thread(new Runnable() { @Override public void run() { test.method2(); } }).start(); } }
The execution results are as follows. Compared with the code segment, it is obvious that thread 2 needs to wait for the method1 execution of thread 1 to complete before starting to execute method2 method.
Method 1 start Method 1 execute Method 1 end Method 2 start Method 2 execute Method 2 end
Static method (class) synchronization
Code segment 3:
public class SynchronizedTest { public static synchronized void method1(){ System.out.println("Method 1 start"); try { System.out.println("Method 1 execute"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 1 end"); } public static synchronized void method2(){ System.out.println("Method 2 start"); try { System.out.println("Method 2 execute"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 2 end"); } public static void main(String[] args) { final SynchronizedTest test = new SynchronizedTest(); final SynchronizedTest test2 = new SynchronizedTest(); new Thread(new Runnable() { @Override public void run() { test.method1(); } }).start(); new Thread(new Runnable() { @Override public void run() { test2.method2(); } }).start(); } }
The execution results are as follows. The synchronization of static methods is essentially the synchronization of classes (static methods are essentially methods belonging to classes, not methods on objects). Therefore, even if test and test2 belong to different objects, they both belong to the instance of synchronized test class. Therefore, method1 and method2 can only be executed sequentially, not concurrently.
Method 1 start Method 1 execute Method 1 end Method 2 start Method 2 execute Method 2 end
Code block synchronization
Code segment 4:
public class SynchronizedTest { public void method1(){ System.out.println("Method 1 start"); try { synchronized (this) { System.out.println("Method 1 execute"); Thread.sleep(3000); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 1 end"); } public void method2(){ System.out.println("Method 2 start"); try { synchronized (this) { System.out.println("Method 2 execute"); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 2 end"); } public static void main(String[] args) { final SynchronizedTest test = new SynchronizedTest(); new Thread(new Runnable() { @Override public void run() { test.method1(); } }).start(); new Thread(new Runnable() { @Override public void run() { test.method2(); } }).start(); } }
The execution results are as follows. Although thread 1 and thread 2 enter the corresponding method to start execution, thread 2 needs to wait for the execution of the synchronization block in thread 1 to complete before entering the synchronization block.
Method 1 start Method 1 execute Method 2 start Method 1 end Method 2 execute Method 2 end