Javaでsynchronizedの排他制御を試す
Javaでsynchronizedメソッドで排他制御を試したメモです。
Javaでsynchronizedメソッドでのスレッドの排他制御を確認してみました。
- synchronizedメソッド
- 確認用クラス
- 確認
- インスタンスメソッドとインスタンスメソッド
- インスタンスメソッドとsynchronizedインスタンスメソッド
- synchronizedインスタンスメソッドとsynchronizedインスタンスメソッド
- 異なるsynchronizedインスタンスメソッド
- 異なるインスタンスのsynchronizedインスタンスメソッド
- 異なるインスタンスの異なるsynchronizedインスタンスメソッド
- synchronizedインスタンスメソッドとクラスメソッド
- synchronizedインスタンスメソッドとsynchronizedクラスメソッド
- synchronizedクラスメソッドとsynchronizedクラスメソッド
- 異なるsynchronizedクラスメソッド
- 異なるインスタンスから異なるsynchronizedクラスメソッド
synchronizedメソッド
synchronizedをメソッドに付与すると、メソッド実行時にそのオブジェクトのロックを取得できます。
これはsynchronizedブロックでそのオブジェクトを指定したものと同義です。
ロックを取得している間、他のスレッドはそのオブジェクトのロックを取得できません。
そのため同時に1つのスレッドからしか実行できません。
メソッド終了時にそのオブジェクトのロックを開放します。
確認用クラス
MyClassクラスを定義し、下記メソッドを定義しました。
・インスタンスメソッド
・synchronized インスタンスメソッドA
・synchronized インスタンスメソッドB
・クラスメソッド
・synchronized クラスメソッドA
・synchronized クラスメソッドB
MyClass.java
public class MyClass { public void myInstanceMethod() { System.out.println(Thread.currentThread().getName() + ": instanceMethod start"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ": instanceMethod end"); } public synchronized void syncInstanceMethodA() { System.out.println(Thread.currentThread().getName() + ": syncInstanceMethodA start"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ": syncInstanceMethodA end"); } public synchronized void syncInstanceMethodB() { System.out.println(Thread.currentThread().getName() + ": syncInstanceMethodB start"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ": syncInstanceMethodB end"); } public static void myStaticMethod() { System.out.println(Thread.currentThread().getName() + ": staticMethod start"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ": staticMethod end"); } public static synchronized void syncStaticMethodA() { System.out.println(Thread.currentThread().getName() + ": syncStaticMethodA start"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ": syncStaticMethodA end"); } public static synchronized void syncStaticMethodB() { System.out.println(Thread.currentThread().getName() + ": syncStaticMethodB start"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ": syncStaticMethodB end"); } }
確認
マルチスレッドのテストは難しく、完全な安全性を保証することは出来ないですが、
動作確認用の簡易テストで確認しました。
インスタンスメソッドとインスタンスメソッド
synchronizedでないインスタンスメソッドは複数のスレッドから同時実行可能。
ロックを取得しないので、複数スレッドから実行可能です。
@Test public void 同時実行可_インスタンスメソッド() throws InterruptedException { MyClass clazz = new MyClass(); Runnable task = clazz::myInstanceMethod; new Thread(task).start(); new Thread(task).start(); Thread.sleep(5000); }
テスト結果
Thread-0: instanceMethod start Thread-1: instanceMethod start Thread-1: instanceMethod end Thread-0: instanceMethod end
インスタンスメソッドとsynchronizedインスタンスメソッド
synchronizedでないインスタンスメソッドとsynchronizedインスタンスメソッドは
複数のスレッドから同時実行可能。
synchronizedでないインスタンスメソッドはロックを取得しないので、
他のsynchronizedメソッドのロックにかかわらず実行可能です。
@Test public void 同時実行可_インスタンスメソッドとsynchronizedメソッド() throws InterruptedException { MyClass clazz = new MyClass(); Runnable taskNonSync = clazz::myInstanceMethod; Runnable taskSync = clazz::syncInstanceMethodA; new Thread(taskNonSync).start(); new Thread(taskSync).start(); Thread.sleep(5000); }
テスト結果
Thread-0: instanceMethod start Thread-1: syncInstanceMethodA start Thread-1: syncInstanceMethodA end Thread-0: instanceMethod end
synchronizedインスタンスメソッドとsynchronizedインスタンスメソッド
synchronizedインスタンスメソッドは複数のスレッドから同時実行不可。
synchronizedはそのインスタンスのロックを取得するため、同時実行できません。
@Test public void 同時実行不可_synchronizedメソッド() throws InterruptedException { MyClass clazz = new MyClass(); Runnable taskSync = clazz::syncInstanceMethodA; new Thread(taskSync).start(); new Thread(taskSync).start(); Thread.sleep(5000); }
テスト結果
Thread-0: syncInstanceMethodA start Thread-0: syncInstanceMethodA end Thread-1: syncInstanceMethodA start Thread-1: syncInstanceMethodA end
異なるsynchronizedインスタンスメソッド
異なるsynchronizedインスタンスメソッドは複数のスレッドから同時実行不可。
synchronizedはそのインスタンスのロックを取得するため、異なるメソッドでも同時実行できません。
@Test public void 同時実行不可_異なるsynchronizedメソッド() throws InterruptedException { MyClass clazz = new MyClass(); Runnable taskSyncA = clazz::syncInstanceMethodA; Runnable taskSyncB = clazz::syncInstanceMethodB; new Thread(taskSyncA).start(); new Thread(taskSyncB).start(); Thread.sleep(5000); }
テスト結果
Thread-0: syncInstanceMethodA start Thread-0: syncInstanceMethodA end Thread-1: syncInstanceMethodB start Thread-1: syncInstanceMethodB end
異なるインスタンスのsynchronizedインスタンスメソッド
異なるインスタンスのsynchronizedインスタンスメソッドは複数のスレッドから同時実行可能。
synchronizedはそのインスタンスのロックを取得するため、インスタンスが異なればロックも異なるため
同時に実行できます。
@Test public void 同時実行可_異なるインスタンスのsynchronizedメソッド() throws InterruptedException { MyClass clazz1 = new MyClass(); MyClass clazz2 = new MyClass(); Runnable taskClazz1 = clazz1::syncInstanceMethodA; Runnable taskClazz2 = clazz2::syncInstanceMethodA; new Thread(taskClazz1).start(); new Thread(taskClazz2).start(); Thread.sleep(5000); }
テスト結果
Thread-0: syncInstanceMethodA start Thread-1: syncInstanceMethodA start Thread-1: syncInstanceMethodA end Thread-0: syncInstanceMethodA end
異なるインスタンスの異なるsynchronizedインスタンスメソッド
異なるインスタンスの異なるsynchronizedインスタンスメソッドは複数のスレッドから同時実行可能。
synchronizedはそのインスタンスのロックを取得するため、インスタンスが異なればロックも異なるため
同時に実行できます。
@Test public void 同時実行可_異なるインスタンスの異なるsynchronizedメソッド() throws InterruptedException { MyClass clazz1 = new MyClass(); MyClass clazz2 = new MyClass(); Runnable taskClazz1 = clazz1::syncInstanceMethodA; Runnable taskClazz2 = clazz2::syncInstanceMethodB; new Thread(taskClazz1).start(); new Thread(taskClazz2).start(); Thread.sleep(5000); }
テスト結果
Thread-0: syncInstanceMethodA start Thread-1: syncInstanceMethodB start Thread-0: syncInstanceMethodA end Thread-1: syncInstanceMethodB end
synchronizedインスタンスメソッドとクラスメソッド
synchronizedインスタンスメソッドとクラスメソッドは複数のスレッドから同時実行可能。
クラスメソッドはsynchronizedでないため同時に実行できます。
@Test public void 同時実行可_synchronizedメソッドとstaticメソッド() throws InterruptedException { MyClass clazz = new MyClass(); Runnable taskSync = clazz::syncInstanceMethodA; Runnable taskStatic = MyClass::myStaticMethod; new Thread(taskSync).start(); new Thread(taskStatic).start(); Thread.sleep(5000); }
テスト結果
Thread-0: syncInstanceMethodA start Thread-1: staticMethod start Thread-0: syncInstanceMethodA end Thread-1: staticMethod end
synchronizedインスタンスメソッドとsynchronizedクラスメソッド
synchronizedインスタンスメソッドとsynchronizedクラスメソッドは複数のスレッドから同時実行可能。
インスタンスのロックとクラスのロックは異なるため、同時に実行できます。
@Test public void 同時実行可_synchronizedメソッドとstaticSynchronizedメソッド() throws InterruptedException { MyClass clazz = new MyClass(); Runnable taskSync = clazz::syncInstanceMethodA; Runnable taskStaticSync = MyClass::syncStaticMethodA; new Thread(taskSync).start(); new Thread(taskStaticSync).start(); Thread.sleep(5000); }
テスト結果
Thread-0: syncInstanceMethodA start Thread-1: syncStaticMethodA start Thread-0: syncInstanceMethodA end Thread-1: syncStaticMethodA end
synchronizedクラスメソッドとsynchronizedクラスメソッド
synchronizedクラスメソッドとsynchronizedクラスメソッドは複数のスレッドから同時実行不可。
synchronizedでそのクラスのロックを取得するため、同時実行できません。
@Test public void 同時実行不可_staticSynchronizedメソッド() throws InterruptedException { Runnable taskStaticSyncA = MyClass::syncStaticMethodA; new Thread(taskStaticSyncA).start(); new Thread(taskStaticSyncA).start(); Thread.sleep(5000); }
テスト結果
Thread-0: syncStaticMethodA start Thread-0: syncStaticMethodA end Thread-1: syncStaticMethodA start Thread-1: syncStaticMethodA end
異なるsynchronizedクラスメソッド
異なるsynchronizedクラスメソッドは複数のスレッドから同時実行不可。
異なるsynchronizedクラスメソッドでも、同じクラスのロックを取得するため、同時実行できません。
@Test public void 同時実行不可_異なるstaticSynchronizedメソッド() throws InterruptedException { Runnable taskStaticSyncA = MyClass::syncStaticMethodA; Runnable taskStaticSyncB = MyClass::syncStaticMethodB; new Thread(taskStaticSyncA).start(); new Thread(taskStaticSyncB).start(); Thread.sleep(5000); }
テスト結果
Thread-0: syncStaticMethodA start Thread-0: syncStaticMethodA end Thread-1: syncStaticMethodB start Thread-1: syncStaticMethodB end
異なるインスタンスから異なるsynchronizedクラスメソッド
異なるインスタンス経由で異なるsynchronizedクラスメソッドは複数のスレッドから同時実行不可。
異なるインスタンス経由でもsynchronizedで同じクラスのロックを取得するため、同時実行できません。
@Test public void 同時実行不可_異なるインスタンスから異なるstaticSynchronizedメソッド() throws InterruptedException { MyClass clazz1 = new MyClass(); MyClass clazz2 = new MyClass(); Runnable taskStaticSync1 = () -> { clazz1.syncStaticMethodA(); }; Runnable taskStaticSync2 = () -> { clazz2.syncStaticMethodB(); }; new Thread(taskStaticSync1).start(); new Thread(taskStaticSync2).start(); Thread.sleep(5000); }
テスト結果
Thread-0: syncStaticMethodA start Thread-0: syncStaticMethodA end Thread-1: syncStaticMethodB start Thread-1: syncStaticMethodB end
テストコードは下記にあげました。
github.com
終わり。