ホーム > Java > 第10章-3  

Java -同期化-

オブジェクトのロック

複数のスレッドが同時に処理を行う場合、同時にアクセルされることを防ぎ、一連の処理を保証するためオブジェクトをロックします。 このように同時にアクセルされることを防ぐことを同期化といいます。

同期化するためにsynchonizedキーワードを利用
synchonized指定ができるのはオブジェクトとメソッドのみです。

同じクラス内の複数のメソッドに対してsynchonized指定をした場合、あるスレッドにより1つのsynchonizedメソッド中は、 それ以外のすれべてのsynchonized指定されたメソッドにもロックがかかり待機状態となります。

1つのオブジェクトに対して1つの「ロックブラグ(モニタ)」を取得する必要があります。
「ロックフラグ」を取得したスレッドのみがオブジェクトに対するsynchonizedメソッドの実行権を持ち、 他のスレッドは待機状態になります。
ロックフラグを持つスレッドはsynchonizedメソッドを実行し終わると、ロックフラグを解除します。

通常、宣下する側で同期化を行います。

同期化はロックをかけるため、必要最低限にする必要があります。

スレッドのstatic変数へのアクセス

static変数(クラス変数)はオブジェクトに依存せず、クラスに1つ存在する変数の為、static変数の値は、 スレッド間で共有することになります。
その場合、スレッドの実行順は決まっていないため、同時にアクセスしてしまったり、データの不整合を起こしたりする可能性があります。

static変数のロック

static メソッドにもsynchonized指定することが可能です。 ただし、より狭い範囲でsynchonized指定をしたい場合に利用するsynchonizedブロックには、注意が必要です。
synchonizedブロックの引数にオブジェクトを指定しても、static変数のロックはできません。 その代わりにjava.lang.Classクラスを利用します。

  • staticメソッドのsynchronized指定 []は省略可能です。
    [public] synchronized static 戻り値 メソッド名 (引数) {・・・・・・}
  • オブジェクトのsynchronized指定
    synchronized ( クラス名.class ) {・・・・・・}

クラス名.classの指定をクラスリテラルと呼び、クラス名に指定したClassクラスのインスタンスを表わします。
クラス名.classの代わりにClass.forName("クラス名")と記述しても同じインスタンスを表わすことができます。

デッドロック

同期化では、デッドロックに注意する必要があります。

デッドロックとは、2つ以上のスレッドがお互いにロックを取得し、互いのロック解除待ちになっている状態です。
そのためどちらかのロックが解除されない限り、どちらのスレッドも実行されないので、2つのスレッドが永久に待機したままになります。

複数のスレッドで同じリソースにロックをかける場合、同じ順番でロックするようにすると、デッドロックを避けられることが多いです。

waitメソッド、notifyメソッド、notifyAllメソッド

synchronizedによる同期化を行うことで、オブジェクトに対して複数のスレッドが同時に処理を行い、整合性を保証することはできますが、 他のスレッドが予想外な動作をした場合にも対処する必要があります。

wait、notify、notifyAllメソッドによる待ち合わせ

javaでは待ち合わせのためにjava.lang.Objectクラスのwaitメソッド、notifyメソッド、notifyAllメソッドを利用します。

メソッド 説明
  • void wait() throws InterruptedException
  • void wait(long timeout) throws InterruptedException
  • void wait(long timeout,int nanos) throws InterruptedException
他のスレッドがこのオブジェクトのnotifyメソッドまたはnotifyAllメソッドを呼び出すか、 他のスレッドが現在のスレッドに割り込みをかけるなど、指定された時間が経過するまで、現在のスレッドを待機させます。
waitメソッドにより、待機中のメソッドはロックが階乗されます。
  • void notify()
waitメソッドにより待機中のスレッドを1つ再開します。
  • void notifyAll()
waitメソッドにより待機中のスレッドのすべてを再開します。

wait ,notify,notifyAllメソッドは、必ずsynchronizedメソッドまたはsynchronizedブロック内で利用する必要があります。

オブジェクトに対してロックを取得していない場合、wait,notify,notifyAllメソッドを呼び出すことはできません。
ロックを取得せずにwait,notify,notifyAllメソッドを呼び出そうとすると、実行時にIllegalMonitorStateException例外が発生します。