2012年3月5日月曜日

Java標準同期化オブジェクトの活用で手軽にマルチスレッド対応 | 株式会社シンメトリック公式ブログ - 携帯開発から生まれる技術情報

Java標準同期化オブジェクトの活用で手軽にマルチスレッド対応 | 株式会社シンメトリック公式ブログ - 携帯開発から生まれる技術情報

前回は、DateFormatクラスがマルチスレッド問題によってバグを引き起こす例を紹介した。マルチスレッドによる問題に対処するには、スレッドごとにインスタンスを作成するか、synchronizedブロックによる同期化を行う。でも「正しい同期化」を行うには十分な知識とコードの把握が不可欠・・・。

そんな時に重宝するのが、Java標準で用意されている同期化オブジェクト。どんなケースにも対応できるわけではないけど、お手軽かつ必要十分な機能を備えている。これを使わない手はない。

第3の同期化方法

前回は同期化の方法として、以下2通りの方法を紹介した。

  • [方法A]スレッドごとにクラスのインスタンスを生成
  • [方法B]クラスのメソッド呼び出しの同期化

今回紹介するのは、同期化オブジェクトによりラップするという方法。実はこの方法は、分類としては[方法B]の中に入る。ArrayListクラスのJavaDocを見てみると、そのやり方が記載されている。


MB- D200方法電池の種類を変更する
この実装は同期化されません。複数のスレッドが同時に ArrayList のインスタンスにアクセスし、1 つ以上のスレッドが構造的にリストを変更する場合には、リストを外部的に同期化する必要があります。構造的な変更とは、1 つ以上の要素を追加または削除したり、基になる配列のサイズを明示的に変更したりする処理のことです。 要素の値だけを設定する処理は、構造的な変更ではありません。通常、リストの同期をとるには、リストを自然にカプセル化するオブジェクトで同期をとります。 この種のオブジェクトがない場合には、Collections.synchronizedList メソッドを使用してリストを「ラップ」する必要があります。これは、リストへの偶発的な非同期アクセスを防ぐために、作成時に行うのが最適です。 ArrayListのJavaDoc

ArrayListの場合はCollectionsクラスのメソッドを使うことで、同期化オブジェクトによるラップを行うことができる。ArrayList以外では、HashMapやHashSetなどJava標準のコレクションクラスに対しても、この方法を使うことができる。使うのも簡単だ。

同期化オブジェクトによるラップとは?

まずはArrayListを使用してスレッド問題が発生するServletを例として挙げてみたい。このServletは、アクセスするとブラウザに1が表示され、アクセスするたびに1ずつカウントアップしていく、というもの。ただし、同期化を行っていないため、アクセスが集中すると例外が発生したり、数字がカウントアップしなかったりすることがある。

ソースコードは以下の通り。


携帯電話、携帯電話の目的は何ですか?
import java.io.IOException; import java.util.*; import javax.servlet.*; import javax.servlet.http.*;   public class TestServlet extends HttpServlet {   private static final List list = new ArrayList();    @Override   protected void doGet(HttpServletRequest request, HttpServletResponse response)      throws ServletException, IOException {     // 同期化なし     list.add("hoge");     response.setContentType("text/plain");     response.getWriter().write(list.size());   } }

この例を使って、まずは以下3つの方法を比較して考えてみる。

  1. 同期化を行わない方法
  2. 呼び出し側でメソッド呼び出しの同期化を行う方法(方法B)
  3. ラップオブジェクトでメソッド呼び出しの同期化を行う方法

1.同期化を行わない方法

ArrayListのaddメソッド呼び出しに対し、同期化を全く行わない場合を考えてみる。この場合はServletへのアクセスが集中すると、次のようにArrayList#ensureCapacityメソッド内で例外が発生することがある。

他にもスレッドの動作タイミングによっては、リストにエントリーが追加されない誤動作をする場合もある。

2.呼び出し側でメソッド呼び出しの同期化を行う方法(方法B)

synchronizedブロックにより、ArrayList#addメソッド呼び出しを同期化する場合、マルチスレッド問題は発生しない。ソースコードは次のようになる。


最高のデジタルビデオカメラは何ですか
import java.io.IOException; import java.util.*; import javax.servlet.*; import javax.servlet.http.*;   public class TestServlet extends HttpServlet {   private static final List list = new ArrayList();    @Override   protected void doGet(HttpServletRequest request, HttpServletResponse response)      throws ServletException, IOException {     // synchronizedブロックによる呼び出し側での同期化     synchronized (list) {       list.add("hoge");       response.setContentType("text/plain");       response.getWriter().write(list.size());     }   } }

動作は次のようになる。

 

3.ラップオブジェクトでメソッド呼び出しの同期化を行う方法

いよいよラップオブジェクトを使う方法の登場。ソースコードの変更箇所は、ArrayListをインスタンス化している部分だけだ。new ArrayList() でインスタンス化した後、これをCollections#synchronizedListメソッドの引数に渡してラップしたListを取得する。

import java.io.IOException; import java.util.*; import javax.servlet.*; import javax.servlet.http.*;   public class TestServlet extends HttpServlet {   // ラップオブジェクトによる同期化   private static final List list =      Collections.synchronizedList(new ArrayList());    @Override   protected void doGet(HttpServletRequest request, HttpServletResponse response)      throws ServletException, IOException {     list.add("hoge");     response.setContentType("text/plain");     response.getWriter().write(list.size());   } }

たったこれだけで同期化処理は完了。 同期化はどこで行われているかというと、次のようにCollectionsクラスの内部クラスSynchronizedRandomAccessListが同期化を代行してくれる。


Collectionsクラスの内部クラスSynchronizedRandomAccessListは、Collections#synchronizedListメソッド呼び出し時にインスタンス化される。そしてこのSynchronizedRandomAccessListクラスはListインタフェースを実装していて、addメソッド/getメソッドなどほぼ全てのメソッドがsynchronizedブロックにより同期化されている。Collections#synchronizedListメソッドの戻り値はこのSynchronizedRandomAccessListクラスのインスタンスだから、以後addメソッドを呼び出す度に同期化が行われることになる。

public class Collections {   public static  List list) {     return (list instanceof RandomAccess ?       new SynchronizedRandomAccessList(list) :       new SynchronizedList(list));   }    static class SynchronizedCollection implements Collection, Serializable {     Collection c;     Object	   mutex;     SynchronizedCollection(Collection c) {       if (c==null)         throw new NullPointerException();       this.c = c;       mutex = this;     }       : (中略)   }    static class SynchronizedList     extends SynchronizedCollection     implements List {     List list;      SynchronizedList(List list) {       super(list);       this.list = list;     }     // ほぼすべてのメソッドがsynchronizedブロックで同期化されている     public void add(int index, E element) {       synchronized(mutex) {list.add(index, element);}     }     public E remove(int index) {       synchronized(mutex) {return list.remove(index);}     }       : (中略)   }    static class SynchronizedRandomAccessList     extends SynchronizedList     implements RandomAccess {     SynchronizedRandomAccessList(List list) {       super(list);     }       : (中略)   }

ラップオブジェクトのメリット・デメリット

このように同期化オブジェクトでラップする方法には次のようなメリットがある。


  • インスタンス作成時にラップするだけで使用できる
  • 同期化オブジェクトがListインタフェースなど同一のインタフェースを実装しているため、呼び出し側の修正がほぼ不要
  • 全てのメソッド呼び出しが同期化される

一方、次のような注意事項もある。

  • 同期化する必要がないインスタンスに対して使用するとパフォーマンスが劣化する
  • ラップをすることにより、元のオブジェクトを直接参照することが困難となる(Collections#synchronizedListメソッドの戻り値は別クラスのインスタンスとなるため)
  • getメソッド呼び出しからaddメソッド呼び出しまで、といったように複数メソッドで構成される一連の操作を同期化することはできない

制限事項はあるとはいっても、悩ましいマルチスレッド処理がとにかく簡単・安全に対応できるのが最大のメリットだ。



These are our most popular posts:

医学書院/書籍・電子メディア/見てできる褥瘡のラップ療法

ラップ療法は、治療効果がよく、処置も簡単で低コストであることから、現在広く行われる ケア方法になっています。しかし、こういった質問が少なくないところをみると、実際には 基本的なポイントが理解されていないのではないかと感じます。 日本褥瘡学会の隆盛 ... read more

Java標準同期化オブジェクトの活用で手軽にマルチスレッド対応

2008年5月19日 ... ArrayListの場合はCollectionsクラスのメソッドを使うことで、同期化オブジェクトによる ラップを行うことができる。ArrayList以外では、HashMapやHashSetなどJava標準の コレクションクラスに対しても、この方法を使うことができる。使うのも ... read more

レーススタートの行程

土曜に予選が行われた結果でグリッド(=レース開始順位)が決まるわけですが,レース を開始するにあたり,まず一度この順番に車を整列させます。この順番のことを「ダミー グリッド」といいます。 フォーメーションラップ開始30分前から15分前までに,予選の 合算 ... read more

「不適切な湿潤療法による被害 いわゆる"ラップ療法"の功罪」

2011年4月6日 ... 日本において近年、創傷管理の基本理論のひとつのキーワードとして挙げられている moist wound healingだが、その理論をもとに食品用ラップを用いて簡便に湿潤環境を 作り出す「ラップ療法」が広く行われている。しかし、その理論の誤認 ... read more

Related Posts



0 コメント:

コメントを投稿