2013-06-12 2 views
3

Мне нужен способ разрешить только одному потоку изменять данные, относящиеся к билету на обслуживание. В течение более чем одного потока может быть предпринята попытка изменить данные билета одновременно.Синхронизация потоков на основе идентификатора

Ниже приведен упрощенный вариант моего подхода. Есть лучший способ сделать это? Может быть, с пакетами java.util.concurrent?

public class SomeClass1 
{ 
    static final HashMap<Integer, Object> ticketLockMap = new HashMap<Integer, Object>(); 


    public void process(int ticketNumber) 
    { 
     synchronized (getTicketLock(ticketNumber)) 
     { 
      // only one thread may modify ticket data here 

      // ... ticket modifications here... 
     } 
    } 


    protected static Object getTicketLock(int ticketNumber) 
    { 
     Object ticketLock; 

     // allow only one thread to use map 
     synchronized (ticketLockMap) 
     { 
      ticketLock = ticketLockMap.get(ticketNumber); 

      if (ticketLock == null) 
      { 
       // first time ticket is locked 
       ticketLock = new Object(); 
       ticketLockMap.put(ticketNumber, ticketLock); 
      } 
     } 

     return ticketLock; 
    } 
} 

Кроме того, если я не хочу, наполняя HashMap с неиспользуемыми замков, я бы нужен более сложный подход, как следующее:

public class SomeClass2 
{ 
    static final HashMap<Integer, Lock> ticketLockMap = new HashMap<Integer, Lock>(); 


    public void process(int ticketNumber) 
    { 
     synchronized (getTicketLock(ticketNumber)) 
     { 
      // only one thread may modify ticket data here 

      // ... ticket modifications here... 

      // after all modifications, release lock 
      releaseTicketLock(ticketNumber); 
     } 
    } 


    protected static Lock getTicketLock(int ticketNumber) 
    { 
     Lock ticketLock; 

     // allow only one thread to use map 
     synchronized (ticketLockMap) 
     { 
      ticketLock = ticketLockMap.get(ticketNumber); 

      if (ticketLock == null) 
      { 
       // first time ticket is locked 
       ticketLock = new Lock(); 
       ticketLockMap.put(ticketNumber, ticketLock); 
      } 
     } 

     return ticketLock; 
    } 


    protected static void releaseTicketLock(int ticketNumber) 
    { 
     // allow only one thread to use map 
     synchronized (ticketLockMap) 
     { 
      Lock ticketLock = ticketLockMap.get(ticketNumber); 

      if (ticketLock != null && --ticketLock.inUseCount == 0) 
      { 
       // lock no longer in use 
       ticketLockMap.remove(ticketLock); 
      } 
     } 
    } 
} 


class Lock 
{ 
    // constructor/getters/setters omitted for brevity 
    int inUseCount = 1; 
} 
+2

Вы должны иметь возможность использовать что-то вроде 'ConcurrentHashMap' вместо' HashMap' для блокировки билетов и избегать бит синхронизации, который плохо работает. 'putIfAbsent()' должно быть именно то, что вам нужно. – manub

+0

@Jeff проверить эту тему http://stackoverflow.com/questions/659915/synchronizing-on-an-integer-value –

ответ

1

Вы могли бы искать для Lock interface. Второй случай можно решить с помощью ReentrantLock, который подсчитывает количество блокировок.

Замки имеют .lock() метод, который ждет замок, чтобы приобрести и .unlock метод, который должен быть вызван, как

Lock l = ...; 
l.lock(); 
try { 
    // access the resource protected by this lock 
} finally { 
    l.unlock(); 
} 

Это тогда можно было бы объединить с HashMap<Integer, Lock>. Вы можете опустить вызовы synchronized и сократить строки кода.

Смежные вопросы