1

У меня есть ConcurrentHashMap и метод, который помещает String в карту, тогда я делаю некоторые действия в синхронизированном блоке на основе вставленного значения.ConcurrentHashMap putIfAbsent первый раз

putIfAbsentвозвращает предыдущее значение, связанное с указанным ключом, или нулевое значение, если не было никакого отображения для ключа - на основе официальной документации

Есть 2 действия, которые выполняются на основе того, обнулить putIfAbsent возвращается или нет.

Теперь вот трюк. Я хочу первое действие (когда 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"); 
      }   

} 
} 

Примечание: Кроме того, убедитесь, что вы удалите записи из HashMaps, когда идентификатор удаляется

или с помощью другого поля (кроме строки идентификатора), чтобы синхронизировать код

0

иногда (около 5% времени) Я получаю это сообщение на консоли:

У вас есть условие гонки, которое первое, что нужно добавить, не является первым для печати.

В этом случае вашим основным узким местом является использование System.out, это контент-ресурс, который во много раз дороже, чем использование Карты, одновременной или иной.

В этом случае, вы можете также упростить код, чтобы вы получите только один замок, который является замком на System.out который вы должны получить в любом случае

// 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 предлагает другие механизмы синхронизации, которые предлагают большую степень детализации , и ИМО, ясность.

Рассмотрите приведенный ниже код. Код иллюстрирует (а), как защитить несколько операций с помощью блокировки (b), как можно обрабатывать разделы then и else по-разному (например, 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 "); 
     }   
    } 
} 
Смежные вопросы