Thread覚え書き その2
すっかり忘れているので、調べたことの覚え書きその2です。
辞書のように、Key,Valueペアの組を扱いたい場合に利用するJavaのクラスにはHashtableとHashMapがあります。Hashtableはnullを受け付けないが、HashMapはnullも受け付ける違い以外にも、Hashtableの各メソッドはほぼ全てがsynchronized宣言されているのに対して、
public synchronized boolean containsKey(Object key) { Entry tab[] = table; int hash = key.hashCode(); ...... }
HashMapのメソッドはsynchronized宣言されていないという違いもあります。
public boolean containsKey(Object key) { return getEntry(key) != null; }
HashMapはスレッドセーフではないのでスレッド間の競合の可能性がある場合は、
Map m = Collections.synchronizedMap(new HashMap(...));
のようにjava.util.collectionsのメソッドを使ってラップして使います。
上記のようにsynchronizingするようにラップしたHashMapはどのようにsynchronizingされるのかも見てみました。Collections.synchronizedMap()メソッドでラップした場合は、引数に指定したHashMapオブジェクトにロックをかけて各メソッドを実行することになります。Collectionsクラスのソースコードを眺めると、
public staticMap synchronizedMap(Map m) { return new SynchronizedMap (m); } .... private static class SynchronizedMap implements Map , Serializable { ...... private final Map m; // Backing Map final Object mutex; // Object on which to synchronize SynchronizedMap(Map m) { if (m==null) throw new NullPointerException(); this.m = m; mutex = this; } .... public boolean containsKey(Object key) { synchronized(mutex) {return m.containsKey(key);} } .....
というように、各メソッドの中でsynchronizingしていますが、synchronizedブロックでは引数で渡されてきたオブジェクトにロックをかけていました。これで、Hashtableと同じようにMap型のオブジェクトの各メソッドもsynchronizingされることになります。
もう一つ、JDK1.5からはjava.util.concurrent.ConcurrentHashMapというスレッドセーフなクラスも使えるようになりました。このクラスはHashtableと同じメソッドを持っていて、Hashtableと同じようにnullを受け付けません。ConcurrentHashMapクラスのソースコードを眺めると、java.util.concurrent.locks.ReentrantLockやjava.util.concurrent.locks.AbstractOwnableSynchronizerを使ってロックしてからメソッドの本来の処理を行っています。こちらは、メソッドを持っているオブジェクトにロックをかけるというよりも、実行しているスレッドに排他的に動ける環境を作ってあげるとうような感じです。
ConcurrentHashMapはスレッドセーフでしかもパフォーマンスが良いといことで、今後はHashtableやsynchronizingしているHashMapよりも、ConcurrentHashMapを使った方がいいとのことでした。