Java併発のsynchronizedロックの内容解析


synchronizedが方法でロックしているのは何ですか?
ロックされているのは現在のオブジェクトの現在の方法であり、他のスレッドがこのオブジェクトにアクセスするためのsynchronized方法またはブロックがブロックされますが、非synchronized方法はブロックされません。
きたなく読む
よくある概念です。マルチスレッドでは、同じオブジェクトのインスタンス変数やグローバル静的変数を複数のスレッドで同時に訪問する場合があります。正しい同期処理をしないと、「汚い読み」という結果になります。注意ここの局部変数は汚い読みがない場合です。

public class ThreadDomain13
{
  private int num = 0;
  
  public void addNum(String userName)
  {
    try
    {
      if ("a".equals(userName))
      {
        num = 100;
        System.out.println("a set over!");
        Thread.sleep(2000);
      }
      else
      {
        num = 200;
        System.out.println("b set over!");
      }
      System.out.println(userName + " num = " + num);
    }
    catch (InterruptedException e)
    {
      e.printStackTrace();
    }
  }
}
二つのスレッドを書いてそれぞれadd文字列「a」と文字列「b」に行きます。

public class MyThread13_0 extends Thread
{
  private ThreadDomain13 td;
  
  public MyThread13_0(ThreadDomain13 td)
  {
    this.td = td;
  }
  
  public void run()
  {
    td.addNum("a");
  }
}
public class MyThread13_1 extends Thread
{
  private ThreadDomain13 td;
  
  public MyThread13_1(ThreadDomain13 td)
  {
    this.td = td;
  }
  
  public void run()
  {
    td.addNum("b");
  }
}
主関数を書いて、それぞれこの二つのスレッドを実行します。

public static void main(String[] args)
{
  ThreadDomain13 td = new ThreadDomain13();
  MyThread13_0 mt0 = new MyThread13_0(td);
  MyThread13_1 mt1 = new MyThread13_1(td);
  mt0.start();
  mt1.start();
}
//       
a set over!
b set over!
b num = 200
a num = 200
正常には「a num=100」と「b num=200」をプリントするべきですが、現在は「b num=200」と「a num=200」をプリントしています。これがスレッドの安全問題です。どうやってスレッドの安全が問題になるか考えてもいいです。
1、mt 0は先に運行して、numに100を賦与して、それから“a set over!”を印刷して、眠り始める
2、mt 0が寝ている間、mt 1が実行され、numに200を与え、「b set over!」そして「b num=200」を印刷します。
3、mt 1は寝終わりました。mt 0のnumとmt 1のnumは同じnumなので、mt 1はnumを200に変えました。mt 0も仕方がないです。numは100しかないです。mt 0は引き続きコードを実行して、「a num=200」をプリントします。
問題が発生した原因を分析したら、解決は簡単です。addNum(String userName)方法に同期を加えるだけでいいです。
マルチスレッドラインのsynchronizedキーワードをメソッドに追加します。

public class ThreadDomain13
{
  private int num = 0;
  
  public synchronized void addNum(String userName)
  {
    try
    {
      if ("a".equals(userName))
      {
        num = 100;
        System.out.println("a set over!");
        Thread.sleep(2000);
      }
      else
      {
        num = 200;
        System.out.println("b set over!");
      }
      System.out.println(userName + " num = " + num);
    }
    catch (InterruptedException e)
    {
      e.printStackTrace();
    }
  }
}
運行結果を見てください。

a set over!
a num = 100
b set over!
b num = 200
複数のオブジェクトに複数の鍵をかける
同期の場合は、main関数内のコードを変更します。

public static void main(String[] args)
{
  ThreadDomain13 td0 = new ThreadDomain13();
  ThreadDomain13 td1 = new ThreadDomain13();
  MyThread13_0 mt0 = new MyThread13_0(td0);
  MyThread13_1 mt1 = new MyThread13_1(td1);
  mt0.start();
  mt1.start();
}
運行結果を見てください。

a set over!
b set over!
b num = 200
a num = 100
ここに重要な概念があります。キーワードsynchronizedで取得されたロックはすべて対象ロックであり、一部のコードや方法(関数)をロックとしてはなく、ここでコードや方法(関数)をロックとして扱う場合、実は取得されたのも対象ロックです。モニタ(オブジェクト)が異なるだけです。どのプログラムが先にsynchronizedキーを持つ方法を実行しますか?どのスレッドがこの方法の対象のロックを持っていますか?他のスレッドは待つしかないです。ただし、この前提はロックを対象ロックと呼ぶ以上、対象とは必ず関連がありますので、複数のスレッドがアクセスするのは同じ対象でなければなりません。
複数のスレッドが複数のオブジェクトにアクセスすると、Java仮想マシンは複数のロックを作成し、上記の例のように、2つのThreadDomain 13オブジェクトを作成し、2つの鍵を生成します。二つのスレッドが異なるロックを持っている以上、当然、「リリース待ち」という行為に制約されず、それぞれアドドNumのコードを実行することができます。
synchronizedがロックしているのは何ですか?
ロックされているのは現在のオブジェクトです。synchronizedブロックの内容が実行された後、現在のオブジェクトのロックが解除されます。同じ時刻に複数のスレッドがこのオブジェクトにアクセスすると、ブロックされます。
synchronizedがロックしているのは何ですか?
鍵をかけているのはobjectの対象です。synchronizedブロックの内容が実行されたら、Objectオブジェクトのロックを解除します。同じ時刻に複数のスレッドがこのオブジェクトにアクセスすると、ブロックされます。
ここで注意したいのは、ObjectがInteger、Stringなどのパッケージクラスの場合(newで出てくる対象は除く)、現在のオブジェクトをロックしたり、スレッドをブロックしたりしないことです。包装類はfinalなので、修正できません。修正すれば新しいオブジェクトが生成されます。したがって、他のスレッドは、スレッドによって修正された後、そのオブジェクトのロックを取得すると、そのオブジェクトは元のオブジェクトではないので、取得したのは別のオブジェクトのロックですので、ブロックは発生しません。
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。