blog

静的メソッドと非静的メソッドの同期化された変更について

私はモディファイア・メソッドの違いについて深く調べたことがなく、この知識は常に曖昧です。インターネットで目にする情報のほとんどは、指示だけで、少し霧がかっています。コピペが多い今日は、一度全体の櫛。 ...

Sep 5, 2020 · 11 min. read
シェア

はじめに

synchronized修飾メソッドの違いについて深く調べたことがなく、この知識はいつも曖昧です。インターネットで目にする情報のほとんどは説明文だけで、ちょっと霧がかっています。コピペが多い今日は全体的なコーミングを一度。

説明

synchronizedは、同期ミューテックス・ロック機能を実装するためのコード・ブロックだけでなく、クラスやメソッドの変更にも使用できます。今回は、クラスの変更とメソッドの変更の側面を対象にしています。つまり、以下のようなケースに分けられます:

状況リスト

I. 同期化されたクラスの変更

) 2つのスレッドが同じオブジェクトのmethod1メソッドを呼び出せるかどうか SynchronizedTest.

public class SynchronizedTest {
 public void method1() {
 synchronized(SynchronizedTest.class) {
 System.out.println(Thread.currentThread().getName() + "先取り1 メソッド,時間:" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
 try {
 Thread.sleep(5000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 System.out.println(Thread.currentThread().getName() + "リリース1メソッド" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
 }
 }
}
public class Test2 {
 public static void main(String[] args) throws ParseException {
 SynchronizedTest s1 = new SynchronizedTest();
 new Thread(s1::method1).start();
 new Thread(s1::method1).start();
 }
}
  • 結果の印刷
Thread-0先取り1 メソッド,時間: 
Thread-0リリース1メソッド 
Thread-1先取り1 メソッド,時間: 
Thread-1リリース1メソッド 

) 2つのスレッドが異なるオブジェクトのメソッド1メソッドを呼び出すことができるかどうか SynchronizedTest.

public class SynchronizedTest {
 public void method1() {
 synchronized(SynchronizedTest.class) {
 System.out.println(Thread.currentThread().getName() + "先取り1 メソッド,時間:" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
 try {
 Thread.sleep(5000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 System.out.println(Thread.currentThread().getName() + "リリース1メソッド" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
 }
 }
}
public class Test2 {
 public static void main(String[] args) throws ParseException {
 SynchronizedTest s1 = new SynchronizedTest();
 SynchronizedTest s2 = new SynchronizedTest();
 new Thread(s1::method1).start();
 new Thread(s2::method1).start();
 }
}
  • 結果の印刷
Thread-0先取り1 メソッド,時間: 
Thread-0リリース1メソッド 
Thread-1先取り1 メソッド,時間: 
Thread-1リリース1メソッド 

) スレッドオブジェクトSynchronizedTestのmethod1メソッドは、他のスレッドから同じオブジェクトの他の同期メソッドや非同期メソッドにアクセスできますか?

public class SynchronizedTest {
 public void method1() {
 try {
 synchronized (SynchronizedTest.class) {
 System.out.println(Thread.currentThread().getName() + "先取り1 メソッド,時間:" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
 Thread.sleep(5000);
 System.out.println(Thread.currentThread().getName() + "リリース1メソッド" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
 }
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 public void method2() {
 try {
 synchronized (SynchronizedTest.class) {
 System.out.println(Thread.currentThread().getName() + "先取り2メソッド、時間:" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
 Thread.sleep(5000);
 System.out.println(Thread.currentThread().getName() + "リリース2のメソッド" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
 }
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 public void method3() {
 try {
 System.out.println(Thread.currentThread().getName() + "先取り3メソッド、時間:" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
 Thread.sleep(5000);
 System.out.println(Thread.currentThread().getName() + "リリース3のメソッド" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
}
public class Test2 {
 public static void main(String[] args) throws ParseException, InterruptedException {
 SynchronizedTest s1 = new SynchronizedTest();
 Thread thread1 = new Thread(s1::method1);
 thread1.setName(" 1");
 thread1.start();
 Thread thread2 = new Thread(s1::method2);
 thread2.setName(" 2");
 thread2.start();
 Thread thread3 = new Thread(s1::method3);
 thread3.setName(" 3");
 thread3.start();
 }
}
  • 結果の印刷
スレッド3は3つのメソッドをプリエンプトしている。: 
スレッド1が1つのメソッドをプリエンプトしている。: 
スレッド3リリース3メソッド 
スレッド 1 リリース 1 メソッド 
スレッド2は2つのメソッドをプリエンプトしている。: 
スレッド 2 リリース 2 メソッド 
  • 説明

    synchronizedがクラスを変更すると、実際には.classがロックされ、クラスとオブジェクトの関係は図面とオブジェクトのようなものになります。すべてのSynchronizedTestオブジェクトは同じ.classを使用しているため、直接的な相互排除があります。異なるオブジェクトであるかどうかは関係ありません。また、クラスとクラス内部メソッドの関係は、同じように見える家がたくさんあり、それぞれに玄関があり、中に部屋がたくさんある近所の家の関係に少し似ています。synchronizedがクラスを修正するとき、それはこの隣人が鍵をかけられていて、鍵は1つしかないので、誰かがこの隣人の中に入ると、隣人は再び閉じられ、外で待っている人たちはただ待つしかないということに似ています。

同期化された非静的メソッドの修正

public class SynchronizedTest {
 public synchronized void method1() {
 System.out.println(Thread.currentThread().getName() + "先取り1 メソッド,時間:" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
 try {
 Thread.sleep(5000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 System.out.println(Thread.currentThread().getName() + "リリース1メソッド" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
 }
 public void method2() {
 System.out.println(Thread.currentThread().getName() + "2つのメソッドのプリエンプト" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
 try {
 Thread.sleep(5000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 System.out.println(Thread.currentThread().getName() + "リリース2のメソッド" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
 }
}

) あるスレッドがSynchronizedTestのmethod1メソッドを占有している場合、別のスレッドは同じオブジェクトのSynchronizedTestのmethod1メソッドにアクセスできますか。

public class Test2 {
 public static void main(String[] args) throws ParseException {
 SynchronizedTest s1 = new SynchronizedTest();
 new Thread(s1::method1).start();
 new Thread(s1::method1).start();
 }
}
  • 結果の印刷
Thread-0先取り1 メソッド,時間: 
Thread-0リリース1メソッド 
Thread-1先取り1 メソッド,時間: 
Thread-1リリース1メソッド 
  • 説明

    synchronizedが非静的メソッドを変更する場合、実際にロックされるのは対応するオブジェクト、正確には割り当てられたメモリです。上記の場合、同じオブジェクト、つまりメモリは1つです。例えば、近所に一軒の家があり、二人がAという部屋に入りたいとします。しかし、この家の鍵のかかっている部屋はすべて部屋のドアに対応する鍵を共有しており、鍵は一つしかありません。

) あるスレッドがSynchronizedTestのmethod1メソッドを使用しているとき、別のスレッドが同じオブジェクトのSynchronizedTestのmethod2メソッドにアクセスできますか。

public class Test2 {
 public static void main(String[] args) throws ParseException {
 SynchronizedTest s1 = new SynchronizedTest();
 new Thread(s1::method1).start();
 new Thread(s1::method2).start();
 }
}
  • 結果の印刷
Thread-12つのメソッドのプリエンプト 
Thread-0先取り1 メソッド,時間: 
Thread-1リリース2のメソッド 
Thread-0リリース1メソッド 
  • 説明

    1)と同じようなもの。上と同じ例えですが、違うのはこの家ではAという部屋だけ鍵がかかっていて、Bという部屋は鍵が開いているので、Bという部屋は無防備で自由に出入りできるのと同じです。鍵は関係ありません。しかし、もしBの部屋も鍵がかかっていたら、つまりmethod2も同期で変更されていたら、李斯はまだBの部屋に行って張三がいつAの部屋に入るか見たいのに、それができません。だから、あなたはまだ張三が出てくるのを待つ必要があります。

) あるスレッドがSynchronizedTestのmethod1メソッドを占有しているとき、別のスレッドが別のオブジェクトのSynchronizedTestのmethod1メソッドにアクセスできますか。

public class Test2 {
 public static void main(String[] args) throws ParseException {
 SynchronizedTest s1 = new SynchronizedTest();
 SynchronizedTest s2 = new SynchronizedTest();
 new Thread(s1::method1).start();
 new Thread(s2::method1).start();
 }
}
  • 結果の印刷
Thread-0先取り1 メソッド,時間: 
Thread-0リリース1メソッド 
Thread-1先取り1 メソッド,時間: 
Thread-1リリース1メソッド 
  • 説明

    と1)は、ここでは新しい2つのオブジェクトが異なっている、つまり、メモリが異なっています。または上記の類推は、近所に似たような家Aがあり、部屋Aの家がロックされ、張さんは家に入るために鍵を取ったが、全く同じ家の建物の隣に、李Siは2番目の家Bに、この新しい家はまだ入力されていないので、2番目の人はまだ家のロックされた部屋Aに入ることができます。

) あるスレッドがSynchronizedTestのmethod1メソッドを使用しているとき、別のスレッドが別のオブジェクトのSynchronizedTestのmethod2メソッドにアクセスできますか。

public class Test2 {
 
 public static void main(String[] args) throws ParseException {
 SynchronizedTest s1 = new SynchronizedTest();
 SynchronizedTest s2 = new SynchronizedTest();
 new Thread(s1::method1).start();
 new Thread(s2::method2).start();
 }
}
  • 結果の印刷
Thread-12つのメソッドのプリエンプト 
Thread-0先取り1 メソッド,時間: 
Thread-1リリース2のメソッド 
Thread-0リリース1メソッド 
  • 説明

    理屈は上の3)と同じで、オブジェクトは同じではないので、それぞれのオブジェクトの内部メソッドを呼び出せばいいのですが、3との違いは、2番目の人がB家の2番目の部屋に行ったというだけです。そしてこの部屋は鍵がかかっていません。

III.同期化された静的メソッドの変更

public class SynchronizedTest {
 
 public synchronized static void method1() {
 System.out.println(Thread.currentThread().getName() + "先取り1 メソッド,時間:" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
 try {
 Thread.sleep(5000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 System.out.println(Thread.currentThread().getName() + "リリース1メソッド" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
 }
 
 public void method2() {
 System.out.println(Thread.currentThread().getName() + "2つのメソッドのプリエンプト" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
 try {
 Thread.sleep(5000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 System.out.println(Thread.currentThread().getName() + "リリース2のメソッド" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
 }
}

) あるスレッドがSynchronizedTestのmethod1メソッドを占有していますが、別のスレッドが同時にこのmethod1メソッドにアクセスすることは可能ですか?

public class Test2 {
 public static void main(String[] args) throws ParseException {
 new Thread(SynchronizedTest::method1).start();
 new Thread(SynchronizedTest::method1).start();
 }
}
  • 結果の印刷
Thread-0先取り1 メソッド,時間: 
Thread-0リリース1メソッド 
Thread-1先取り1 メソッド,時間: 
Thread-1リリース1メソッド 
  • 説明

    まず、静的メソッドとfee静的メソッドの違いは、ほんの数点です:

    静的メソッド:と静的変数は、このクラスに属し、マシンがメモリにロードされるクラスの初期化では、クラスの呼び出しを通じて、それが使用されていない場合でも、jvmはクリアされません。

    非静的メソッドは:オブジェクトのインスタンスに属し、オブジェクトがメモリを確保するためにインスタンス化されたときだけでなく、インスタンスを介してメソッドを呼び出すには、インスタンスでjvmの回復時間もリサイクルされます。

    上記の紹介によると、同期が静的メソッドを変更すると、静的メソッドとクラスのバインディングは、つまり、クラスによって生成されたすべてのインスタンスは、このメソッドに共通です。例えて言えば、静的メソッドは、上記の近所の家のように、近所のすべての家のための共通の駐車場のようです。そこで、チャン・サンとリー・シーの二人が駐車場に車を停めに行こうとすると、駐車場には鍵がかかっていて、その鍵はセルと同じ鍵です。ブロックの鍵は開いていますが、チャン・サンはこの鍵を開けているので、リー・シーは外で待つしかありません。

) あるスレッドがSynchronizedTestのmethod1メソッドを使用しているとき、別のスレッドがmethod2メソッドにアクセスできますか。

public class Test2 {
 public static void main(String[] args) throws ParseException {
 SynchronizedTest s1 = new SynchronizedTest();
 new Thread(SynchronizedTest::method1).start();
 new Thread(s1::method2).start();
 }
}
  • 結果の印刷
Thread-12つのメソッドのプリエンプト 
Thread-0先取り1 メソッド,時間: 
Thread-1リリース2のメソッド 
Thread-0リリース1メソッド 
  • 説明

    張三は鍵を手に入れて駐車場に入りましたが、他の人は無施錠でB室に入ったので、誰でも入ることができます。もしB室が非静的方式でロックされていたら、つまり方式2が同期された後だったらどうでしょう。李詩はまた入ることができますか?答えはイエスです。静的メソッドロックはクラスロックに似ていますが、非静的メソッドロックは実はオブジェクトロックであり、クラスロックとオブジェクトロックは別物です。つまり、コミュニティゲート、駐車場などの家の門と家の中の部屋のロックは同じではなく、キーを共有することはできません、簡単に言えば、張三は、駐車場に入るために駐車場のロックを開くためにコミュニティゲートの鍵を取って、李斯は、ロックされた部屋Bを開くために家Aの鍵を取った。

概要

  1. synchronizedがスタティック・メソッドを変更する場合、すべてのオブジェクトで共有されるクラス・ロックとして解釈できます。
  2. synchronizedが非静的メソッドを変更する場合、それはオブジェクト・ロックとして解釈され、現在のオブジェクトによって使用され、そのオブジェクト内のすべてのロックされたメソッドによって共有されます。
  3. クラスロックとオブジェクトロックは共存するものではありません。
Read next

CentOS 7 複数インスタンスでTomcatをスタンドアロン・インストールする

Apache Tomcatのシングル・マルチインスタンス配備方式とは、1台のサーバにマスター・アプリケーションを1セットだけインストールし、複数のアプリケーション・インスタンスを起動する方式です。この方式は主に次のような場合に使用されます: シングルインスタンス単一アプリケーション: 最も一般的なタイプの配備シナリオで、webappsディレクトリにドロップするwarパッケージを提供するだけです。 シングルインスタンスのマルチアプリケーション: 複数のウェブアプリケーションが複数の WAR パッケージを生成し、これらの WAR を統一して ...

Sep 5, 2020 · 6 min read