2011-10-10 58 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

不好,你很好:每個map-entry是一個單獨的對象,所以map-entry上的同步不會在地圖上同步,也不會在擁有map的類上同步。

(順便說一句,你#getMapEntry(...)方法的實際返回,而不是進入。映射條目同時包含一個鍵和一個值的引用。)

+0

如果用戶是地圖的關鍵,那麼關鍵的結構改變如何?這是不安全的? –

+1

他不插入並獲取用戶? – MasterCassim

+0

(注意:Hemal Pandya和MasterCassim正在回答我的答案的早期版本,這是完全錯誤的:我完全誤解了這個問題。) – ruakh

0

這應該工作;第二個塊只能通過此用戶進行同步。

小測試:

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

你是不是在同步中類對象上,但我是做你認爲它是做什麼:

  • 不同的線程中使用共享s_lock訪問地圖。但我認爲你可以如讓上

  • 不同的線程s_usersfinalsynchronize訪問同一User對象執行「耗時的操作 」的序列。

我看到的唯一一點是如果對象在地圖上保持相同的User更改。只要一個userId只有一個User對象,你就沒問題。

我做了類似的事情,但在userId.intern()上同步。它有它的缺點,但對我的情況很好。