В нашей системе у нас есть метод, который сделает некоторую работу, когда это назовут с определенным идентификатором:
public void doWork(long id) { /* ... */ }
Теперь, эта работа может быть сделана одновременно для различных идентификаторов, но если метод называют с тем же идентификатором 2 потока, один поток должен заблокироваться, пока это не закончено.
Простое решение состояло бы в том, чтобы иметь Карту, которая отображается от Длинного идентификатора до некоторого произвольного объекта, который мы можем соединить. Одна проблема, которую я предвижу с этим, состоит в том, что у нас могут быть тонны идентификаторов в системе, и эта карта будет продолжать расти каждый день.
Идеально, я думаю, что нам нужна система, где каждый из нас распараллеливает, выберет объект блокирования, блокировка, если это возможно, сделайте работу, затем предупредите, что мы сделаны с блокировкой. Если ясно, что никто больше не использует эту конкретную блокировку, то безопасно удаляют его из карты блокировки для предотвращения утечки памяти.
Я предполагаю, что это должно быть довольно общим сценарием, таким образом, я надеюсь, что там существует существующее решение. Кто-либо знает о ком-либо?
Некоторое время назад я изобрел для себя нечто подобное. Я называю это блокировкой класса эквивалентности, то есть блокирует все вещи, которые равны данной вещи. Вы можете получить его из моего github и использовать его в соответствии с лицензией Apache 2, если хотите, или просто прочтите и забудьте!
Вы можете попробовать что-нибудь с ReentrantLock, например, у вас будет Map
. Теперь после lock.release () вы можете протестировать lock.hasQueuedThreads (). Если это вернет false, вы можете удалить его с карты.
Для начала:
Вы говорите здесь о настройке блокировки-полосы. Один конец континуума - это одна гигантская блокировка для всех идентификаторов, что просто и безопасно, но не параллельно. Другой конец - это блокировка каждого идентификатора, которая проста (в некоторой степени) и безопасна и очень параллельна, но может потребовать большого количества "блокируемых объектов" в памяти (если у вас их еще нет). Где-то посередине находится идея создания блокировки для ряда идентификаторов - это позволяет регулировать параллельность в зависимости от окружения и выбирать компромисс между памятью и параллельностью.
ConcurrentHashMap может быть использован для достижения этой цели, поскольку CHM состоит из сегментов (подкарты), и на каждый сегмент приходится одна блокировка. Это дает вам параллелизм, равный количеству сегментов (которое по умолчанию равно 16, но может быть настроено).
Существует множество других возможных решений для разделения пространства ID и создания наборов блокировок, но вы правы в том, что нужно быть чувствительным к проблемам очистки и утечки памяти - забота об этом при сохранении параллелизма - дело непростое. Вам нужно будет использовать некоторый вид подсчета ссылок на каждую блокировку и тщательно управлять вытеснением старых блокировок, чтобы избежать вытеснения блокировки, которая находится в процессе блокировки. Если вы пойдете этим путем, используйте ReentrantLock или ReentrantReadWriteLock (и не синхронизируйтесь с объектами), так как это позволит вам явно управлять блокировкой как объектом и использовать дополнительные методы, доступные для него.
Есть также некоторые материалы по этому вопросу и пример StripedMap в Java Concurrency in Practice раздел 11.4.3.
Не достаточно ли использовать SynchronizedHashMap или Collections. synchronizedMap(Map m) из пакета java.util.concurrent вместо обычного HashMap, где вызовы извлечения и вставки не синхронизированы?
что-то вроде:
Map<Long,Object> myMap = new HashMap<Long,Object>();
Map<Long,Object> mySyncedMap=Collections.synchronizedMap(myMap);
Преждевременная оптимизация - это корень зла
Попробуйте использовать (синхронизированную) карту.
Возможно, если он станет слишком большим, вы сможете регулярно очищать его содержимое.
Убедитесь, что ни один столбец не содержит значений null
в связанной таблице (т. е. в обновляемой таблице).
Можно сделать так, как это делали пакеты чертежей старых, и отобразить растягиваемую линейку. Пусть пользователи перетаскивают виртуальную линейку до тех пор, пока она не будет соответствовать физической линейке, которую они поместили на экран.
Не является серьезным предложением для использования в производстве, но, вероятно, единственный способ на самом деле получить правильный ответ: (.
-121--3926368- Здесь можно использовать карту канонизации, которая принимает ввод long
и возвращает канонический объект Long
, который затем можно использовать для синхронизации. Я написал о канонизации карт здесь ; просто замените строку
на Long
(и чтобы облегчить вашу жизнь, пусть в качестве параметра будет использоваться long
).
После создания карты канонизации необходимо записать код, защищенный блокировкой:
Long lockObject = canonMap.get(id);
synchronized (lockObject)
{
// stuff
}
Карта канонизации гарантирует, что для того же идентификатора будет возвращен один и тот же lockObject
. Если нет активных ссылок на lockObject
, они будут доступны для сбора мусора, поэтому вы не будете заполнять память ненужными объектами.
Вы можете попробовать следующий небольшой «прием»
String str = UNIQUE_METHOD_PREFIX + Long.toString(id);
synchornized(str.intern()) { .. }
, который со 100% гарантией вернет тот же самый экземпляр.
UNIQUE_METHOD_PREFIX
может быть жестко запрограммированной константой или может быть получен с помощью:
StackTraceElement ste = Thread.currentThread().getStackTrace()[0];
String uniquePrefix = ste.getDeclaringClass() + ":" +ste.getMethodName();
, что гарантирует, что блокировка произойдет только для этого точного метода. Это для того, чтобы избежать тупиковых ситуаций.
Я бы сказал, что вы уже довольно далеки от решения. Создайте LockManager
, который лениво и со счетчиком ссылок управляет этими блокировками за вас. Затем используйте его в doWork
:
public void doWork(long id) {
LockObject lock = lockManager.GetMonitor(id);
try {
synchronized(lock) {
// ...
}
} finally {
lock.Release();
}
}
Я предлагаю вам использовать утилиты из java.util.concurrent, особенно класс AtomicLong. См. связанную javadoc
Вы можете создать список или набор активных идентификаторов и использовать wait и notify:
List<Long> working;
public void doWork(long id) {
synchronized(working)
{
while(working.contains(id))
{
working.wait();
}
working.add(id)//lock
}
//do something
synchronized(working)
{
working.remove(id);//unlock
working.notifyAll();
}
}
Решенные проблемы:
Проблемы: