【2023】Eight ways to implement synchronized in multi-threading in Java

Introduction

Eight thread locks:
The synchronized keyword is used in eight different ways to implement locks in different places. It is also often asked during the interview process.

1. Act on two different common synchronization methods respectively.

Since synchronized works on three common synchronization methods, in the main method, we create three threads to execute these methods.

  • Since the two methods thread0 and thread2 call the same example0 object, they will use the same lock object (this) , so they will be executed synchronously, and you need to wait for one to be executed before executing the other.

  • Thread1 calls another example1 object. Because synchronized acts on this, it is not mutually exclusive, so there is no need to wait and can be executed asynchronously .

/**
 * @author zhengfuping
 * @version 1.0
 * @description:  两个线程分别访问同一个对象的两个普通方法。
 *                由于synchronized加在非静态方法上,默认锁的是当前对象this,因此两个方法会互斥执行。
 *                在执行的时候第一个方法持有了锁第二个调用的方法无法去获取,
 *                需要等待第一个方法释放锁之后才会去持有锁执行下去。
 * @date 2023/7/20 13:55
 */
public class SynchronizedExample1 {
    
    
    public synchronized void method0(){
    
    
        System.out.println("线程 "+ Thread.currentThread().getName() + " 正在执行 method0.");
        try {
    
    
            Thread.sleep(3000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("线程 " + Thread.currentThread().getName() + " 完成执行 method0.");

    }
    public synchronized void method1(){
    
    
        System.out.println("线程 "+ Thread.currentThread().getName() + " 正在执行 method1.");
        try {
    
    
            Thread.sleep(5000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("线程 " + Thread.currentThread().getName() + " 完成执行 method1.");
    }

    public synchronized void method2(){
    
    
        System.out.println("线程 "+ Thread.currentThread().getName() + " 正在执行 method2.");

        try {
    
    
            Thread.sleep(2000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("线程 " + Thread.currentThread().getName() + " 完成执行 method2.");

    }


    public static void main(String[] args) {
    
    
        SynchronizedExample1 example0 = new SynchronizedExample1();
        SynchronizedExample1 example1 = new SynchronizedExample1();
        Thread thread0 = new Thread(() -> {
    
         //马上执行
            example0.method0();
        });

        Thread thread1 = new Thread(() -> {
    
         //马上执行
            example1.method1();
        });

        Thread thread2 = new Thread(() -> {
    
        //等待thread0 执行完执行
            example0.method2();
        });
        thread0.start();
        thread1.start();
        thread2.start();
    }
}

Results of the:
Insert image description here

2. Act on two different static synchronization methods respectively.

Since synchronized works on static synchronization methods, and the lock used by static methods by default is the Class object of the class , so when the method0, method1, and method2 methods of this class are called in different ways, the same lock needs to be obtained. A Class object lock , so three threads will execute methods in a mutually exclusive manner. When one thread seizes the lock, other threads need to wait for this thread to release the Class lock before they can preempt the acquisition.

/**
 * @author zhengfuping
 * @version 1.0
 * @description: 三个线程分别访问同一个类的三个静态同步方法。由于静态方法默认使用的锁对象是类的Class对象,因此这三个方法会互斥执行。
 * @date 2023/7/20 14:19
 */
public class SynchronizedExample2 {
    
    

    public static synchronized void method0(){
    
    
        System.out.println("线程 "+ Thread.currentThread().getName() + " 正在执行 method0.");
        try {
    
    
            Thread.sleep(1000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("线程 " + Thread.currentThread().getName() + " 完成执行 method0.");

    }

    public static synchronized void method1(){
    
    
        System.out.println("线程 "+ Thread.currentThread().getName() + " 正在执行 method1.");
        try {
    
    
            Thread.sleep(5000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("线程 " + Thread.currentThread().getName() + " 完成执行 method1.");
    }

    public static synchronized void method2(){
    
    
        System.out.println("线程 "+ Thread.currentThread().getName() + " 正在执行 method2.");
        try {
    
    
            Thread.sleep(2000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("线程 " + Thread.currentThread().getName() + " 完成执行 method2.");
    }


    public static void main(String[] args) {
    
    
        SynchronizedExample2 example1 = new SynchronizedExample2();
        SynchronizedExample2 example2 = new SynchronizedExample2();
        Thread thread0 = new Thread(() -> {
    
        //获取到锁
            SynchronizedExample2.method0();
        });
        Thread thread1 = new Thread(() -> {
    
        //等待thread0 释放
            example1.method1();
        });
        Thread thread2 = new Thread(() -> {
    
       //等待thread1 释放 
            example2.method2();
        });

        thread0.start();
        thread1.start();
        thread2.start();
    }
}

Results of the:
Insert image description here

3. Static synchronization method and ordinary synchronization method

If a class locks static synchronized methods and ordinary synchronized methods, several situations will occur:

  • 0 (static), 1 (static), 2 (normal)

    • Thread a acquires the lock of method0, and when thread b requests method1, it needs to wait for thread a to release the lock before it can call and execute (because both of them are static synchronization methods, so the lock is the Class object), and if c The thread new 对象does not need to wait when calling the method2 method, because the method2 method is a normal synchronization method and belongs to this object and will not be locked.
  • 0 (static), 1 (normal), 2 (normal)

    • Thread a acquires the lock of the method0 method, and when the thread b requests the method1 method, it can obtain it directly without waiting, as above; because the lock of the method0 method is the Class object , it cannot lock the ordinary synchronization method, and at this time the c thread wants to When getting the method3 method, **if it is the same new
      object as the b thread, you need to wait for the b thread to release the lock. **If it is a new object, there is no need to wait, because the b thread lock is added to the this object of the b thread and cannot lock other objects.

Basically, it follows a principle: if the object of the static method locks the Class object , all static synchronization methods of this class will be repelled; if the ordinary synchronization method locks the current object of this , it will make the method of the current object call Generate exclusion


/**
 * @author zhengfuping
 * @version 1.0
 * @description: 当a线程访问对象的method2普通同步方法,b线程访问类的method0静态同步方法。由于锁对象不同,因此这两个方法不会互斥执行。
 *                但如果在来一个c线程如果是访问的是类的另外一个method1静态同步方法时,因为method0是静态方法锁的是Class对象,所以c
 *                线程需要等待等待b线程执行完才能够去执行
 * @date 2023/7/20 15:22
 */
public class SynchronizedExample3 {
    
    
    public static synchronized void method0(){
    
    
        System.out.println("线程 "+ Thread.currentThread().getName() + " 正在执行 method0.");

        try {
    
    
            Thread.sleep(3000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("线程 " + Thread.currentThread().getName() + " 完成执行 method0.");

    }
    public static synchronized void method1(){
    
    
        System.out.println("线程 "+ Thread.currentThread().getName() + " 正在执行 method1.");

        try {
    
    
            Thread.sleep(5000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("线程 " + Thread.currentThread().getName() + " 完成执行 method1.");

    }
    public synchronized void method2(){
    
    
        System.out.println("线程 "+ Thread.currentThread().getName() + " 正在执行 method2.");

        try {
    
    
            Thread.sleep(2000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("线程 " + Thread.currentThread().getName() + " 完成执行 method2.");

    }


    public static void main(String[] args) {
    
    
        SynchronizedExample3 example1 = new SynchronizedExample3();
        SynchronizedExample3 example2 = new SynchronizedExample3();
        Thread thread0 = new Thread(() -> {
    
    
            SynchronizedExample3.method0();
        });
        Thread thread1 = new Thread(() -> {
    
    
            example1.method1();
        });
        Thread thread2 = new Thread(() -> {
    
    
            example2.method2();
        });

        thread0.start();
        thread1.start();
        thread2.start();
    }
}

4. Ordinary methods and ordinary code blocks

Like two ordinary methods, mutual exclusion will be generated if they are the same object, because they lock the current instance object of their own, and they will be mutually exclusive when called; if they are not the same object, mutual exclusion will not be generated.

/**
 * @author zhengfuping
 * @version 1.0
 * @description: 同一个对象的普通方法和普通代码块之间也是互斥的(和两个普通方法一样),因为他们使用的是同一个对象 ,
 *               如果是不同的对象的话就不会互斥
 * @date 2023/7/20 16:43
 */
public class SynchronizedExample4 {
    
    

    public synchronized void method0(){
    
    
        System.out.println("线程 "+ Thread.currentThread().getName() + " 正在执行 method0.");
        try {
    
    
            Thread.sleep(2000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("线程 " + Thread.currentThread().getName() + " 完成执行 method0.");

    }

    public void synchronizedBlock1(){
    
    
        synchronized (this){
    
    
            System.out.println("进入同步代码块1");
            try {
    
    
                Thread.sleep(5000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("退出同步代码块1");
        }
    }
    public void synchronizedBlock2(){
    
    
        synchronized (this){
    
    
            System.out.println("进入同步代码块2");
            try {
    
    
                Thread.sleep(5000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("退出同步代码块2");
        }
    }

    public static void main(String[] args) {
    
    
        SynchronizedExample4 example1 = new SynchronizedExample4();
        SynchronizedExample4 example2 = new SynchronizedExample4();

        Thread thread0 = new Thread(example1::method0);
        Thread thread1 = new Thread(example1::synchronizedBlock1);
        Thread thread2 = new Thread(example2::synchronizedBlock2);
        
        thread0.start();
        thread1.start();
        thread2.start();
    }
}

5. Static synchronization methods and static code blocks

When calling the static synchronization method of the Class class or the static synchronization code block, no matter what method is used to call, mutual exclusion will be generated, and it is necessary to wait for the previous thread that acquired the lock to release the lock before acquiring the lock . Because the lock is added to the Class class, you need to wait for the synchronization to complete.

/**
 * @author zhengfuping
 * @version 1.0
 * @description: 静态同步和静态同步代码块也是互斥的,因为他们的锁是加在Class上,所以只要调用的是这个Class类的静态同步方法或者同步代码块,
 *               都会产生互斥,需要等待一个执行完之后才可以执行另外一个。
 * @date 2023/7/20 16:55
 */
public class SynchronizedExample5 {
    
    
    public static synchronized void method0(){
    
    
        System.out.println("线程 "+ Thread.currentThread().getName() + " 正在执行 method0.");
        try {
    
    
            Thread.sleep(2000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("线程 " + Thread.currentThread().getName() + " 完成执行 method0.");

    }

    public static void synchronizedBlock1(){
    
    
        // 静态同步代码块
        synchronized (SynchronizedExample5.class){
    
      
            System.out.println("进入同步代码块1");
            try {
    
    
                Thread.sleep(5000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("退出同步代码块1");
        }
    }
    public static void synchronizedBlock2(){
    
    
        synchronized (SynchronizedExample5.class){
    
    
            System.out.println("进入同步代码块2");
            try {
    
    
                Thread.sleep(5000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("退出同步代码块2");
        }
    }

    public static void main(String[] args) {
    
    
        SynchronizedExample5 example1 = new SynchronizedExample5();

        Thread thread0 = new Thread(()->{
    
       //获取锁
            example1.method0();
        });
        Thread thread1 = new Thread(SynchronizedExample5::synchronizedBlock1);   //等待
        Thread thread2 = new Thread(SynchronizedExample5::synchronizedBlock2);   //等待

        thread0.start();
        thread1.start();
        thread2.start();
    }
}

Execution results: Will be executed synchronously one by one
Insert image description here

6. Ordinary synchronization methods and static code blocks

The reason is that the locking position is different

/**
 * @author zhengfuping
 * @version 1.0
 * @description: 普通同步方法(同步代码块)如果是相同实例对象的则会发生互斥,如果是不同实例对象调用则不会发生互斥
 *               静态同步方法(同步代码块)则不管是什么方式调用都会发生互斥
 * @date 2023/7/20 17:11
 */
public class SynchronizedExample6 {
    
    

    public synchronized void synchronizedMethod1(){
    
    
        // 普通同步方法
        System.out.println("进入普通同步方法1");
        try {
    
    
            Thread.sleep(3000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("退出普通同步方法1");

    }

    public static synchronized void synchronizedMethod2(){
    
    
        // 普通同步方法
        System.out.println("进入静态同步方法2");
        try {
    
    
            Thread.sleep(3000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("退出静态同步方法2");

    }

    public  void synchronizedBlock1(){
    
    
        synchronized (SynchronizedExample5.class){
    
    
            System.out.println("进入同步代码块");
            try {
    
    
                Thread.sleep(3000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("退出同步代码块");
        }
    }
    public static void synchronizedBlock2(){
    
    
        synchronized (SynchronizedExample5.class){
    
    
            System.out.println("进入静态同步代码块2");
            try {
    
    
                Thread.sleep(3000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("退出静态同步代码块2");
        }
    }


    public static void main(String[] args) {
    
    
        SynchronizedExample6 example61 = new SynchronizedExample6();
        SynchronizedExample6 example62 = new SynchronizedExample6();

        Thread thread1 = new Thread(example61::synchronizedBlock1);
        Thread thread2 = new Thread(example62::synchronizedMethod1);
        Thread thread3 = new Thread(SynchronizedExample6::synchronizedBlock2);
        Thread thread4 = new Thread(SynchronizedExample6::synchronizedMethod2);

        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}

Summarize

  • Ordinary synchronization methods (synchronization code blocks) will be mutually exclusive if they are called on the same instance object. If they are called on different instance objects, mutual exclusion will not occur.
  • Static synchronization methods (synchronous code blocks) will be mutually exclusive no matter how they are called and need to wait.
    Insert image description here

Guess you like

Origin blog.csdn.net/weixin_52315708/article/details/131832225