Start with no synchronized

Look at the following code

public class StatefulTest {
    public static void main(String[] args) {
        Runnable stateOfX = () ->{// Implement the Runnable interface
            /* Member variables are shared by multiple threads. An object is said to be stateful if it has modifiable member variables. Conversely, if it has no modifiable member variables, it is called stateless */
            int x = 0;
            while(x! =40){
                System.out.println("x: "+x++);
                try {
                    Thread.sleep((long) (Math.random()*100));
                } catch(InterruptedException e) { e.printStackTrace(); }}};// Access stateOfX through two threads simultaneously
        new Thread(stateOfX).start();
        newThread(stateOfX).start(); }}Copy the code
  • The results

  • It turns out that
    • Both threads get x = 0 at the same time, indicating that x can be shared by multiple threads
    • The same number only appears twice
    • One thread modifies the value, and other threads get the changed value

Use synchronized

code

  • Create an instance object
public class HelloAndWorld {
    public static void main(String[] args) throws InterruptedException {
        Test test = new Test();
        Runnable run1 = test::hello;// Reference the instance method of test
        Runnable run2 = test::world;
        new Thread(run1).start();
        Thread.sleep((long) (Math.random() * 200));// Thread sleep, run1 must start execution first
        newThread(run2).start(); }}class Test{// Synchronized
    public synchronized void hello(a)  {
        try {
            Thread.sleep((long) (Math.random()*1000));
            System.out.println("hello");
        } catch(InterruptedException e) { e.printStackTrace(); }}public synchronized void world(a){
        System.out.println("world"); }}Copy the code

  • Create two instance objects
public class HelloAndWorld {
    public static void main(String[] args) throws InterruptedException {
        Test test = new Test();
        Test test2 = new Test();// Create two real objects
        Runnable run1 = test::hello;
        Runnable run2 = test2::world;// The method that references the second real object
        new Thread(run1).start();
        Thread.sleep((long) (Math.random() * 200));// Thread sleep, run1 must start execution first
        newThread(run2).start(); }}class Test{
    public synchronized void hello(a)  {
        try {
            Thread.sleep((long) (Math.random()*1000));
            System.out.println("hello");
        } catch(InterruptedException e) { e.printStackTrace(); }}public synchronized void world(a){
        System.out.println("world"); }}Copy the code

The above two examples want to illustrate

  • A thread is executing one of the real objectssynchronizedMethod, another threadYou can’t execute itExecuting without threadssynchronizedmethods
    • When a thread executes into synchronized, it acquies the lock on the object, and another thread cannot execute any other method that is modified by synchronized, regardless of whether the thread that acquired the lock on the current object is executing that method
  • When a threadafterasynchronizedMethod, which must release the lock to give another thread a chance to acquire the lock on the current instance object
  • A thread is executing one of the real objectssynchronizedMethod, another threadAnother instance object can be executedthesynchronizedmethods
    • Different instance objects have different locks, that is, one instance object has one lock
    • It is worth mentioning that.synchronized staticMethods belong toClassObject, does not belong to the instance object and its lock has to be counted separately

Analyze the synchronized keyword from a bytecode perspective

Example 1: when decorating a block of code

public class MyTest {
    Object object = new Object();
    public void method(a){
        synchronized (object){
            System.out.println("hello world"); }}}Copy the code
  • withJavapLook at the bytecode instructions

  • summary
  • withsynchronizedWhen you use a keyword to modify a block of code, the instance object in parentheses can be any object, provided that the existing object, whatever object you put in parentheses, will not be affected by the block of code that will be synchronized, okay
  • withsynchronizedA keyword modifies a code block when it is amonitorenterCorresponding to the twomonitorexitBecause one is used to let the thread release the lock when the block ends normally and one is used to let the thread release the lock when the block ends abnormally

Example 2: Manually throw an exception to the synchronized code block

  • Example 1monitorenterCorresponding to the twomonitorexitOne of themmonitorexitTo prepare for the release of the lock if the block ends abnormally
  • Try actively throwing an exception from a synchronized block
public class MyTest {
    Object object = new Object();
    public void method2(a){
        synchronized (object){
            System.out.println("hello world");
            throw new RuntimeException();// Throw an exception}}}Copy the code
  • The bytecode

  • summary
  • Example 1: The lock release mode has two possibilities: abnormal end and normal end, so there are twomonitorexit
  • In example 2, there is only one way to release the lock: to throw an exception (exception terminating) to release the lock, so there is only onemonitorexit
  • In any case, the system will try to get the thread to release the lock on the object

Synchronized, synchronized, synchronized

public class MyTest {
    public synchronized void method3(a){
        System.out.println("hello world"); }}Copy the code
  • The bytecode

  • summary
  • For the synchronized keyword modification method, instead of monitorenter and monitorexit directives, an ACC_SYNCHRONIZED flag appears.
  • The JVM uses the ACC_SYNCHRONIZBD access flag to distinguish whether a method is synchronous; When a method is called, the calling instruction checks whether the method has the ACC_SYNCHRONIZBD flag.
  • If you haveACC_SYNCHRONIZBDFlag, then the executing thread will first hold the object on which the method residesMonitor, and then execute the method body. No other thread can get it during the execution of the methodMonitorWhen the thread finishes executing the method, it will release thisMonitorIn other words, the lock is released as soon as the method executes (Monitor)

Synchronized static = synchronized static = synchronized

public class MyTest {
    public synchronized static void method4(a){
        System.out.println("hello world"); }}Copy the code
  • The bytecode

  • summary
  • Similar to example 3, I will not repeat it