2011-10-10 12 views
1

私はアクセスを同期する必要がある静的マップを持っています。このマップは、ユーザーIDによってキーされます。私は、同じユーザーIDに関連するスレッドだけをブロックできるすべてのスレッドをブロックしないように、同期を最適化したいと考えています。静的マップのエントリとJavaの同期

private static Object s_lock = new Object(); 
private static Map<String,User> s_users = new HashMap(); 
... 
private someMethod() { 
    synchronized(s_lock) 
    { 
     // keeping the global lock for as little as possible 
     user=getMapEntry(); 
    } 
    synchronized(user) <-------- (1) 
    { 
     // time consuming operation 
     // hopefully only blocking threads that relate to same user id. 
    } 
} 
... 
private User getMapEntry(String userId) 
{ 
    if (s_users.containsKey(userId)) { 
     user = s_users.get(userId); 
    } 
    else { 
     user = new User(); 
     user.id = userId; 
     s_users.put(userId, user); 
    } 
    return user; 
} 

私の質問は - (1)私は「グローバル」の同期ロックを保持しているが、s_usersマップが静的であると、私はまだ持っています。つまり、効果的に静的エントリであるわけではないと仮定していますでグローバルロック(つまり、クラスオブジェクトの同期)?

+0

ConcurrentHashMap.putIfAbsent()を使用すると、最初の同期ブロックを回避できます。 –

答えて

1

いいえ、各マップエントリは別々のオブジェクトなので、マップエントリの同期はマップやマップを所有するクラスでは同期されません。

(ちなみに、あなたの#getMapEntry(...)メソッドは、実際にはかなりエントリよりも、を返します。マップエントリをキーにして値に両方の参照を含む。)

+0

ユーザーがマップのキーなら、どのように構造がそのキーを変更できますか?これはどのように危険ですか? –

+1

彼はユーザーを挿入したり取得したりしていませんか? – MasterCassim

+0

(注:Hemal PandyaとMasterCassimは私の答えの以前のバージョンに応答していますが、これは間違っていました。 – ruakh

0

これはうまくいくはずです。 2番目のブロックはこのユーザーだけに同期します。

小テスト:

public class Test { 

    protected class User { 
     String id; 
    } 
    private static Object s_lock = new Object(); 
    private static Map<String,User> s_users = new HashMap<String, User>(); 

    public void someMethod(String id) throws InterruptedException { 
     User user; 
     synchronized(s_lock) 
     { 
      // keeping the global lock for as little as possible 
      user = getMapEntry(id); 
     } 
     synchronized(user) 
     { 
      System.out.println("Waiting for user: "+user.id); 
      Thread.sleep(10000); 
     System.out.println("Finished waiting for user: "+user.id); 
     } 
    } 

    private User getMapEntry(String userId) 
    { 
     User user; 

     if (s_users.containsKey(userId)) { 
      user = s_users.get(userId); 
     } 
     else { 
      user = new User(); 
      user.id = userId; 
      s_users.put(userId, user); 
     } 

     return user; 
    } 

    public static void main(String[] args) throws InterruptedException { 
     final Test test = new Test(); 

     for(int i = 0; i < 10; i++) { 
      final int j = i; 

      new Thread() { 
       public void run() { 
        try { 
         test.someMethod(String.valueOf(j % 5)); 
        } catch (InterruptedException e) {} 
       } 
      }.start(); 
     } 
    } 
} 

出力:

Waiting for user: 0 
Waiting for user: 1 
Waiting for user: 3 
Waiting for user: 2 
Waiting for user: 4 
Finished waiting for user: 0 
Finished waiting for user: 1 
Waiting for user: 1 
Waiting for user: 0 
Finished waiting for user: 3 
Finished waiting for user: 2 
Waiting for user: 3 
Waiting for user: 2 
Finished waiting for user: 4 
Waiting for user: 4 
Finished waiting for user: 0 
Finished waiting for user: 1 
Finished waiting for user: 3 
Finished waiting for user: 2 
Finished waiting for user: 4 

だから、大丈夫です。

0

あなたはクラスオブジェクトにsync'ingされていないが、私はあなたがそれをやっていると思う何をやっている:

  • 異なるスレッドは、共有s_lockを使用して地図にアクセスしています。しかし、私はあなただけのようにも同じUserオブジェクトが順番に「時間のかかる操作 」を実行アクセスしている

  • 別のスレッドでs_usersfinalsynchronizeを作ることができると思います。オブジェクトが同じUser用マップに変化し続けるならば、私は見る注意の

唯一のポイントです。あなたが1つだけUserのオブジェクトを持っている限り、userIdあなたは大丈夫です。

私はこれに類似していますが、userId.intern()で同期しました。それには欠点がありますが、私の場合はうまくいきました。

関連する問題