[2023] Java でのマルチスレッドでの同期の 8 つの実装

導入

8 つのスレッド ロック:
synchronized キーワードは、さまざまな場所にロックを実装する 8 つの異なる方法で使用され、面接プロセスでもよく質問されます。

1. 2 つの異なる一般的な同期方法にそれぞれ基づいて動作する

synchronized は 3 つの一般的な同期メソッドに作用するため、main メソッドではこれらのメソッドを実行する 3 つのスレッドを作成します。

  • 2 つのメソッド thread0 と thread2 は同じ example0 オブジェクトを呼び出し、同じロック オブジェクト (this) を使用するため、同期的に実行され、一方が実行されるまで待機してから他方を実行する必要があります。

  • Thread1 は別のexample1 オブジェクトを呼び出します。synchronized はこれに対して動作するため、相互排他的ではなく、待機する必要がなく、非同期で実行できます

/**
 * @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();
    }
}

結果:
ここに画像の説明を挿入します

2. 2 つの異なる静的同期方法をそれぞれ実行します。

synchronized は静的同期メソッドで動作し、デフォルトで静的メソッドによって使用されるロックはクラス の Class オブジェクトであるため、このクラスのメソッド 0、メソッド 1、メソッド 2 が異なる方法で呼び出される場合は、同じロックを使用する必要があります。クラス オブジェクト ロック。そのため、3 つのスレッドが相互排他的な方法でメソッドを実行します。1 つのスレッドがロックを捕捉すると、他のスレッドは取得をプリエンプトする前に、このスレッドがクラス ロックを解放するのを待つ必要があります

/**
 * @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();
    }
}

結果:
ここに画像の説明を挿入します

3. 静的同期方式と通常の同期方式

クラスが静的同期メソッドと通常の同期メソッドをロックしている場合、次のような状況が発生します。

  • 0 (静的)、1 (静的)、2 (通常)

    • スレッド a がメソッド 0 のロックを取得し、スレッド b がメソッド 1 を要求すると、スレッド a がロックを解放してから呼び出して実行する必要があります (どちらも静的同期メソッドであるため、ロックは Class オブジェクトです)。スレッドがnew 对象メソッド 2 メソッドを呼び出すとき、メソッド 2 メソッドは通常の同期メソッドであり、this オブジェクトに属し、ロックされないため、待つ必要はありません。
  • 0 (静的)、1 (通常)、2 (通常)

    • スレッド a がメソッド 0 メソッドのロックを取得し、スレッド b がメソッド 1 を要求すると、上記と同様に待たずに直接ロックを取得できます。メソッド 0 メソッドのロックは Class オブジェクトをロックするため、通常の同期メソッドをロックできませ。今回、スレッド c は、method3 メソッドを取得しようとするとき、**それが
      b スレッドと同じ新しいオブジェクトである場合、b スレッドがロックを解放するまで待つ必要があります。**新しいオブジェクトの場合は、b スレッドのこのオブジェクトに b スレッドのロックが追加され、他のオブジェクトはロックできないため、待つ必要はありません。

基本的に、これは原則に従います:静的メソッドのオブジェクトが Class オブジェクトをロックする場合、このクラスのすべての静的同期メソッドは除外されます。通常の同期メソッドがこの現在のオブジェクトをロックする場合、現在のオブジェクトのメソッドが呼び出されます。メソッドが呼び出されたとき。除外の生成


/**
 * @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. 通常のメソッドと通常のコードブロック

2 つの通常のメソッドと同様、それらが同じオブジェクトである場合、それらは独自の現在のインスタンス オブジェクトをロックするため相互排他が発生し、呼び出されたときに相互排他的になります。同じオブジェクトでない場合、相互排他は発生しません。除外。

/**
 * @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. 静的同期メソッドと静的コード ブロック

この Class クラスの静的同期メソッドまたは静的同期コード ブロックを呼び出す場合、どのように呼び出しても相互排他が発生し、ロックを取得した前スレッドがロックを解放するまで待つ必要があります。ロックロックは Class クラスに追加されるため、同期が完了するまで待つ必要があります。

/**
 * @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();
    }
}

実行結果:1つずつ同期的に実行されます
ここに画像の説明を挿入します

6. 通常の同期方法と静的コードブロック

理由はロック位置が違うからです

/**
 * @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();
    }
}

要約する

  • 通常の同期メソッド (同期されたコード ブロック) は、同じインスタンス オブジェクトによって呼び出された場合は相互に排他的であり、異なるインスタンス オブジェクトによって呼び出された場合には発生しません。
  • 静的同期メソッド (同期コード ブロック) では、どのメソッドが呼び出されても相互排他が発生し、待機する必要があります。
    ここに画像の説明を挿入します

おすすめ

転載: blog.csdn.net/weixin_52315708/article/details/131832225