1

我有一個ConcurrentHashMap和一個方法,在地圖中放入一個字符串,然後我根據插入的值在一個同步塊中執行一些操作。ConcurrentHashMap putIfAbsent第一次

putIfAbsent返回與指定鍵關聯的先前值,或者null如果沒有映射的關鍵 - 根據官方文檔

但是也有一些基於putIfAbsent是否返回空執行2個行動或不。

現在這裏是訣竅。我想要第一個動作(當putIfAbsent返回null)首先被執行,並且所有其他線程被擱置。我的代碼在95%的時間內按預期工作。

private final ConcurrentHashMap<String, String> logins = new ConcurrentHashMap<>(); 

public void login(String id){ 
     String inserted=logins.putIfAbsent(id,id); 

     synchronized(logins.get(id)){ 
      if(inserted==null){ 
       System.out.println("First login"); 
      }else{ 
       System.out.println("Second login"); 
      }   
     } 
} 

如果我把這種方法用在不同的線程login("some_id");有時相同的字符串值(周圍的5%的時間),我得到的控制檯此消息:

Second login 
First login 

什麼我需要改變要始終確保First login是首先執行的?

更新:從我讀的是有可能logins.get(id)返回null,因此同步在一個空對象?

+0

logins.putIfAbsent(id,id)和您的同步塊語句不是原子的。這就是爲什麼有時第一次執行第二次登錄。在字符串文字上同步也不是個好主意 –

+0

map應該是'logins'嗎? –

+0

@MichaelEaster是的。對不起,我修改了代碼 –

回答

0
private final ConcurrentHashMap<String, String> logins= new ConcurrentHashMap<>(); 
private ConcurrentHashMap<String, Object> locks= new ConcurrentHashMap<>(); 


public void login(String id){ 

locks.putIfAbsent(id,new Object()); 
Object lock = locks.get(id); 
synchronized(lock) 
{ 
     String inserted=logins.putIfAbsent(id,id); 

      if(inserted==null){ 
       System.out.println("First login"); 
      }else{ 
       System.out.println("Second login"); 
      }   

} 
} 

注:另外,還要確保你從包含HashMap中刪除條目時,ID被刪除

或使用一些其他領域(除了字符串ID)的代碼

0

有時同步(大約5%的時間)我在控制檯上看到以下消息:

您有競爭條件,首先添加的不是第一個打印。

在這種情況下,您的主要瓶頸是使用System.out,這是一個比使用Map,併發或其他方式昂貴許多倍的資源。

在這種情況下,你可能也簡化代碼,這樣你就只獲得一個鎖哪個是哪個,你必須獲得反正

// use System.out as lock so logging of actions is always in order. 
private final Set<String> ids = Collections.newSetFromMap(new HashMap<>()); 

public void login(String id) { 
    synchronized (System.out) { 
     System.out.println(ids.add(id) ? "First login" : "Second login")l 
    } 
} 
0

Java提供,提供更多的粒度等同步機制上System.out鎖和IMO,清晰度。

考慮下面的代碼。該代碼說明了(a)如何使用鎖定來保護多個操作(b)如何處理不同的部分(例如,then使用鎖定來保護函數; else假定函數不需要保護。情況):

class Task implements Runnable { 
    private String id; 
    private ConcurrentHashMap<String,String> logins; 
    private Lock lock; 

    public Task(String id, ConcurrentHashMap<String,String> logins, Lock lock) { 
     this.id = id; 
     this.logins = logins; 
     this.lock = lock; 
    } 

    public void run() { 
     login(id); 
    } 

    public void login(String id){ 
     lock.lock(); 

     String inserted = logins.putIfAbsent(id,id); 

     if (inserted==null) { 
      System.out.print("First login "); 
      // other functions that require synchronization 
      lock.unlock(); 
     } else { 
      lock.unlock(); 
      // functions that do NOT require synchronization 
      System.out.print("Second login "); 
     }   
    } 
}