■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
                      2009年05月31日

    Java総合講座 - 初心者から達人へのパスポート
                  vol.155

                                セルゲイ・ランダウ
 バックナンバー: http://www.flsi.co.jp/Java_text/
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■


[このメールマガジンは、画面を最大化して見てください。]


========================================================
◆ 01.SOAPのアプリケーション(Webサービス)
========================================================


この同一の資源が同時に複数のスレッドからアクセスされることによる問題
に対処するための一つの簡単な方法は、synchronizedというキーワードを
使う方法です。

synchronizedはすでにvol.072で出てきましたが、そこでは解説しなかったので、
ここで解説することにします。

もともとsynchronizeという言葉は「同期させる」ことを意味する動詞であり、
synchronizedはその形容詞(過去分詞から形容詞に昇格したもの)です。
「同期させる」というのはもっと分かりやすく言うと「タイミングを合わせる」
ということです。
たとえば「シンクロナイズド・スイミング(略してシンクロ)」というのがあり
ますが、これは音楽に「タイミングを合わせて」踊りのようなスイミングをする
スポーツですね。


ただし、Javaの場合は若干ニュアンスが異なり、synchronizedというキーワードを
指定したメソッドやオブジェクトは、複数のスレッドの間で「同時にアクセスされ
ないように」タイミングを合わさせられることになります。
「同時にアクセスされないように」タイミングを合わさせるためには、オブジェクト
やクラスに自動的にロックをかけるという方法がとられています。(正確に言うと、
各オブジェクトやクラスごとにモニター(monitor)と呼ばれるプログラムが裏で
動いており、このモニターにロックがかけられるのだが、細かい話は省略する。)


ではメソッドにsynchronizedを指定した場合から見てみましょう。

メソッドにはインスタンス・メソッド(staticを指定していないメソッド)と
staticメソッド(staticを指定しているメソッド、「クラス・メソッド」ともいう)
の2種類がありますが、それぞれsynchronizedの働きが若干異なります。


まず、synchronizedが指定されたインスタンス・メソッドの場合は、メソッドが
呼び出されるとそのインスタンスにロックをかけてから、メソッドを実行します。
そしてメソッドの実行が終わったらロックを解除します。
ただし、ロックをかける前に、既に他のスレッドによってロックをかけられていな
いか確認し、既にロックがかけられている場合はロックが解除されるまで待たされ
ることになります。

そうすると、このインスタンスのフィールドに同時に複数のスレッドからアクセス
することはできなくなるわけです。(逆に、synchronized指定していないインスタ
ンス・メソッドであれば、複数のスレッドから同時に同じインスタンス・フィールド
にアクセスすることが可能である。)

ただし、一つのインスタンス(Xとする)にロックがかかっていても、別のインスタン
ス(Yとする)にロックがかかっていなければ、同じメソッドであろうとインスタンスY
のものであれば、インスタンスXと同時に実行できることになります。
この場合、staticフィールドなどの共有資源は同時にアクセスされてしまう恐れがあり
ますので、注意が必要です。(インスタンス・フィールドはインスタンスごとに別々
に用意されているので問題ない。)

一方、synchronizedを指定するのが(インスタンス・メソッドではなく)staticメソッド
の場合は、ロックされる対象は、インスタンスではなくクラス全体になります。
したがって、このメソッドが同時に複数実行されることはありえないので、このメソッド
によってstaticフィールドが同時にアクセスされる恐れはありません。

例えば、次のようなクラスCalc1があったとしましょう。

--------------------------------------------------------
public class Calc1 {
   private static int sum = 0;

   public synchronized static int add(int val) {
      System.out.println("現在: sum =" + sum);
      System.out.println("加える数: val =" + val);
      sum += val;
      System.out.println("加算の結果: sum =" + sum);
      return sum;
   }

   public static int reset(int val) {
      sum = val;
      return sum;
   }
}
--------------------------------------------------------

すると、複数のスレッドで同時にこのadd()メソッドを呼び出しても、これらの
メソッドが同時に実行されることはありません。
一つ目のスレッドでこのadd()メソッドを実行しようとした時点でCalc1クラスに
ロックがかけられるので、他のスレッドではこのadd()メソッドの実行が待たされ
ることになります。
そのため、たとえadd()メソッドの中で上記のsumのようなstaticフィールドなどの
共有資源にアクセスしていたとしても、複数のスレッドで同時にadd()メソッドを
実行することができないので、同時にこれらの資源にアクセスすることはできない
のです。

ただし、Calc1の残りのメソッドであるreset()にもsynchronizedを指定して
おかなければなりません。

さもなければ、Calc1にロックがかかっていても、reset()メソッドのほうは
実行できることになり、もしreset()メソッドでもadd()メソッドと同じ資源
にアクセスしていたら(ここではstaticフィールドのsumにアクセスしている)
問題を起す恐れがあります。(add()の実行の途中でsumの値がresetされてしま
う恐れがある。)

つまり、

--------------------------------------------------------
public class Calc1 {
   private static int sum = 0;

   public synchronized static int add(int val) {
      System.out.println("現在: sum =" + sum);
      System.out.println("加える数: val =" + val);
      sum += val;
      System.out.println("加算の結果: sum =" + sum);
      return sum;
   }

   public synchronized static int reset(int val) {
      sum = val;
      return sum;
   }
}
--------------------------------------------------------

のようにしておく必要があります。


では、現在作成しているAxisのアプリケーションでは、DBManagerのconn変数が
同時に複数のスレッドからアクセスされることがまずいのだからDBManagerの
全staticメソッドをsynchronized指定にすればいいかというと、そうではあり
ません。


(次回に続く)


では、今日はここまでにします。



┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
★ホームページ:
      http://www.flsi.co.jp/Java_text/
★このメールマガジンは
     「まぐまぐ(http://www.mag2.com)」
 を利用して発行しています。
★バックナンバーは
      http://www.flsi.co.jp/Java_text/
 にあります。
★このメールマガジンの登録/解除は下記Webページでできます。
      http://www.mag2.com/m/0000193915.html
★このメールマガジンへの質問は下記Webページにて受け付けて
 います。わからない所がありましたら、どしどしと質問をお寄
 せください。
      http://www.flsi.co.jp/Java_text/
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

Copyright (C) 2009 Future Lifestyle Inc. 不許無断複製