Javaマルチスレッド同時プログラミングSynchronizedキーワード


synchronizedキーワード解析
同期ロックはオブジェクトに依存しています。各オブジェクトに同期ロックがあります。
既存のメンバ変数Testは、スレッドAがTestのsynchronized方法を呼び出すと、スレッドAがTestの同期ロックを獲得するとともに、スレッドBもTestのsynchronized方法を呼び出すと、スレッドBはTestの同期ロックを獲得できなくなり、スレッドAがTestの同期ロックをリリースするのを待つ必要があります。
以上より、正確にsynchronizedキーワードを使用すると、原子性が確保されます。
synchronizedキーワードの特性応用
特性1:
スレッドAがあるオブジェクトのsynchronized方法またはsynchronizedコードブロックを呼び出すと、同期ロックが解除されない場合、他のスレッドが同じオブジェクトのsynchronized方法またはsynchronizedコードブロックを呼び出すと、スレッドAがそのオブジェクトの同期ロックを解除するまでブロックされます。
DEMO 1、synchronized方法:

public class Test {

  private static class Counter {

    public synchronized void count() {
      for (int i = 0; i < 6; i++) {
        System.out.println(Thread.currentThread().getName() + ", i = " + i);
      }
    }

  }

  private static class MyThread extends Thread {

    private Counter mCounter;

    public MyThread(Counter counter) {
      mCounter = counter;
    }

    @Override
    public void run() {
      super.run();
      mCounter.count();
    }
  }

  public static void main(String[] var0) {
    Counter counter = new Counter();
    //  :myThread1   myThread2          counter
    MyThread myThread1 = new MyThread(counter);
    MyThread myThread2 = new MyThread(counter);
    myThread1.start();
    myThread2.start();
  }

}
DEMO 1出力:

Thread-0, i = 0
Thread-0, i = 1
Thread-0, i = 2
Thread-0, i = 3
Thread-0, i = 4
Thread-0, i = 5
Thread-1, i = 0
Thread-1, i = 1
Thread-1, i = 2
Thread-1, i = 3
Thread-1, i = 4
Thread-1, i = 5
DEMO 2、synchronizedコードブロック:

public class Test {

  private static class Counter {

    public void count() {
      synchronized (this) {
        for (int i = 0; i < 6; i++) {
          System.out.println(Thread.currentThread().getName() + ", i = " + i);
        }
      }
    }
  }

  private static class MyThread extends Thread {

    private Counter mCounter;

    public MyThread(Counter counter) {
      mCounter = counter;
    }

    @Override
    public void run() {
      super.run();
      mCounter.count();
    }
  }

  public static void main(String[] var0) {
    Counter counter = new Counter();
    MyThread myThread1 = new MyThread(counter);
    MyThread myThread2 = new MyThread(counter);
    myThread1.start();
    myThread2.start();
  }
}
DEMO 2出力:

Thread-0, i = 0
Thread-0, i = 1
Thread-0, i = 2
Thread-0, i = 3
Thread-0, i = 4
Thread-0, i = 5
Thread-1, i = 0
Thread-1, i = 1
Thread-1, i = 2
Thread-1, i = 3
Thread-1, i = 4
Thread-1, i = 5
同期ロックが解放されないと、他のスレッドは同期ロックが得られるまでブロックされます。
また、DEMO 1とDEMO 2の出力結果は同じであり、synchronized方法とsynchronizedコードブロックの違いは、synchronized方法の作用領域が大きく、全体的な方法に作用し、synchronizedコードブロックは具体的な作用領域を制御し、より正確な制御により効率を向上させることである。詰まるのは時間ですからね。
DEMO 3は、mainメソッドのみを修正します。

public static void main(String[] var0) {
    //   :myThread1   myThread2     Counter         
    MyThread myThread1 = new MyThread(new Counter());
    MyThread myThread2 = new MyThread(new Counter());
    myThread1.start();
    myThread2.start();
  }
DEMO 3出力:

Thread-0, i = 0
Thread-1, i = 0
Thread-0, i = 1
Thread-1, i = 1
Thread-1, i = 2
Thread-1, i = 3
Thread-0, i = 2
Thread-1, i = 4
Thread-0, i = 3
Thread-1, i = 5
Thread-0, i = 4
Thread-0, i = 5
同期ロックは対象に基づいており、ロックの出所が一致すれば、同期の役割を果たすことができます。したがって、オブジェクトが異なると、同期効果は得られません。
特性2:
スレッドAがあるオブジェクトのsynchronized方法またはsynchronizedコードブロックを呼び出すと、同期ロックが解除されない場合、他のスレッドが同じオブジェクトの他のsynchronized方法またはsynchronizedコードブロックを呼び出すと、スレッドAがそのオブジェクトの同期ロックを解除するまでブロックされます。注意:ポイントはその他)
DEMO 4、doOtherThings方法の修正のみ:

public class Test {

  private static class Counter {

    public synchronized void count() {
      System.out.println(Thread.currentThread().getName() + " sleep");
      try {
        Thread.sleep(3000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println(Thread.currentThread().getName() + " awake");
    }

    public synchronized void doOtherThings(){
      System.out.println(Thread.currentThread().getName() + " doOtherThings");
    }
  }

  public static void main(String[] var0) {
    final Counter counter = new Counter();
    new Thread(new Runnable() {
      @Override
      public void run() {
        counter.count();
      }
    }).start();
    new Thread(new Runnable() {
      @Override
      public void run() {
        counter.doOtherThings();
      }
    }).start();
  }
}
DEMO 4出力:

Thread-0 sleep
Thread-0 awake
Thread-1 doOtherThings
このように、synchronizedによって得られた同期ロックは、コードのみをロックするのではなく、オブジェクト全体をロックするものであることが分かります。
この時happens-before原則に言及すべきで、happens-before原則の存在があってこそ、この現象が発生します。
happens-before原則の一つ:
管理ロック原則:一つのロック操作は先に後ろの同じロックに対するロック操作に発生します。
(ここは説明しすぎないで、説明したらもう一つの文章が書けるようになりました)
DEMO 5、doOtherThings方法のみを変更します。

public void doOtherThings(){
      synchronized (this){
        System.out.println(Thread.currentThread().getName() + " doOtherThings");
      }
    }
DEMO 5出力:

Thread-0 sleep
Thread-0 awake
Thread-1 doOtherThings
DEMO 4とDEMO 5の出力結果が一致しました。間違いなく、彼らの同期ロックのソースが一致しているため、同期効果が得られます。

//     synchronized         
public synchronized void count(){};
public void doOtherThings(){
    synchronized (this){}
}
DEMO 6、doOtherThingsメソッドの同期キーワードを削除します。

public void doOtherThings(){
      System.out.println(Thread.currentThread().getName() + " doOtherThings");
    }
DEMO 6出力:

Thread-0 sleep
Thread-1 doOtherThings
Thread-0 awake
スレッドAがあるオブジェクトのsynchronized方法またはsynchronizedコードブロックを呼び出すと、同期ロックが解放されるかどうかにかかわらず、他のスレッドが同じオブジェクトの他の非synchronized方法または非synchronizedコードブロックを呼び出すと直ちに起動することができます。
インスタンスロックとグローバルロック
以上のDEMOが実装されているのはインスタンスロックです。ロックされているのは特定のオブジェクトの例です。
グローバルロックとは何ですか?
クラス全体をロックします。オブジェクトやインスタンスではありません。
注:一例タイプのインスタンスロックは、グローバルロックに該当しません。
グローバルロックの実現:
静的なsynchronized方法
DEMO 7:

public class Test {

  private static class Counter {

    public static synchronized void count() {
      System.out.println(Thread.currentThread().getName() + " sleep");
      try {
        Thread.sleep(3000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println(Thread.currentThread().getName() + " awake");
    }

    public static synchronized void doOtherThings(){
      System.out.println(Thread.currentThread().getName() + " doOtherThings");
    }
  }

  public static void main(String[] var0) {
    new Thread(new Runnable() {
      @Override
      public void run() {
        Counter.count();
      }
    }).start();
    new Thread(new Runnable() {
      @Override
      public void run() {
        Counter.doOtherThings();
      }
    }).start();
  }
}
DEMO 7出力:

Thread-0 sleep
Thread-0 awake
Thread-1 doOtherThings
static声明の方法は大域的な方法であり、対象の実例化には関係ないので、static synchronized方法は大域的な同期方法であり、対象の実例化には関係ない。
synchronized具体的なクラスのコードブロック
DEMO 8:

public class Test {

  private static class Counter {

    public static synchronized void count() {
      System.out.println(Thread.currentThread().getName() + " sleep");
      try {
        Thread.sleep(3000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println(Thread.currentThread().getName() + " awake");
    }

    public void doOtherThings(){
      synchronized (Counter.class){
        System.out.println(Thread.currentThread().getName() + " doOtherThings");
      }
    }
  }

  public static void main(String[] var0) {
    new Thread(new Runnable() {
      @Override
      public void run() {
        Counter.count();
      }
    }).start();
    new Thread(new Runnable() {
      @Override
      public void run() {
        Counter counter = new Counter();
        counter.doOtherThings();
      }
    }).start();
  }
}
DEMO 8出力:

Thread-0 sleep
Thread-0 awake
Thread-1 doOtherThings
synchronized(Counter.class)が獲得した同期ロックは大域的であり、static synchronizedが獲得した同期ロックも大域的で、同じロックなので、同期効果があります。
synchronizedとsynchronizedを区別します。
DEMO 9:

public class Test {

  private static class Counter {

    public void count() {
      synchronized (this){
        System.out.println(Thread.currentThread().getName() + " sleep");
        try {
          Thread.sleep(3000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " awake");
      }
    }

    public void doOtherThings(){
      synchronized (Counter.class){
        System.out.println(Thread.currentThread().getName() + " doOtherThings");
      }
    }
  }

  public static void main(String[] var0) {
    final Counter counter = new Counter();
    new Thread(new Runnable() {
      @Override
      public void run() {
        counter.count();
      }
    }).start();
    new Thread(new Runnable() {
      @Override
      public void run() {
        counter.doOtherThings();
      }
    }).start();
  }
}
DEMO 9出力:

Thread-0 sleep
Thread-1 doOtherThings
Thread-0 awake
synchronizedが獲得したのは具体的な対象例のcounterのロックであり、synchronizedが獲得したのはグローバルロックであり、二つの異なるロックがあるため、同期効果は達成できない。