Я не могу придумать способ сделать это, который действительно отражает вашу идею блокировки пары объектов.Какой-то низкоуровневый параллелизм мог бы изобрести его, но у меня есть сомнения, есть ли у нас необходимые примитивы для его реализации в Java.
Я думаю, что идея использовать пары в качестве ключей для идентификацииблокировка объектов хорошая.Если вы хотите избежать блокировки, организуйте поиск так, чтобы он ничего не делал.
Я бы предложил двухуровневую карту, примерно такую:
Map<DatabaseIdentifier, Map<ActivityIdentifier, Lock>> locks;
Используется неопределенно, таким образом:
synchronized (locks.get(databaseIdentifier).get(activityIdentifier)) {
performSpecificActivityOnDatabase();
}
Если вы знаете, что все базы данных и действия являются начальными, то просто создайте совершенно нормальную карту, содержащую все комбинации при запуске приложения, и используйте ее точно так же, как описано выше.Единственная блокировка - это объекты блокировки, и здесь нет разногласий.
Если вы не знаете, какими будут базы данных и действия, или слишком много комбинаций для создания полной карты заранее, тогда вынужно будет создавать карту постепенно.Вот где начинаются параллельные времена веселья.
Простое решение заключается в том, чтобы лениво создавать внутренние карты и блокировки и защищать эти действия с помощью обычных блокировок:
Map<ActivityIdentifier, Object> locksForDatabase;
synchronized (locks) {
locksForDatabase = locks.get(databaseIdentifier);
if (locksForDatabase == null) {
locksForDatabase = new HashMap<ActivityIdentifier, Object>();
locks.put(databaseIdentifier, locksForDatabase);
}
}
Object lock;
synchronized (locksForDatabase) {
lock = locksForDatabase.get(locksForDatabase);
if (lock == null) {
lock = new Object();
locksForDatabase.put(locksForDatabase, lock);
}
}
synchronized (lock) {
performSpecificActivityOnDatabase();
}
Как вы, очевидно, видитеосознавая, это приведет к слишком большому количеству раздоров.Я упоминаю это только для дидактической полноты.
Вы можете улучшить это, сделав внешнюю карту параллельной:
ConcurrentMap<DatabaseIdentifier, Map<ActivityIdentifier, Object>> locks;
И:
Map<ActivityIdentifier, Object> newHashMap = new HashMap<ActivityIdentifier, Object>();
Map<ActivityIdentifier, Object> locksForDatabase = locks.putIfAbsent(databaseIdentifier, newHashMap);
if (locksForDatabase == null) locksForDatabase = newHashMap;
Object lock;
synchronized (locksForDatabase) {
lock = locksForDatabase.get(locksForDatabase);
if (lock == null) {
lock = new Object();
locksForDatabase.put(locksForDatabase, lock);
}
}
synchronized (lock) {
performSpecificActivityOnDatabase();
}
Ваша единственная блокировка там.будет на картах для каждой базы данных, в течение периода размещения и получения, и, согласно вашему отчету, этого не будет много.Вы можете преобразовать внутреннюю карту в ConcurrentMap
, чтобы избежать этого, но это звучит как перебор.
Тем не менее, будет создаваться устойчивый поток из HashMap
экземпляров, которые будут поданы в putIfAbsent
а затем выбрасывают.Вы можете избежать этого с помощью своего рода постмодернистского атомарного ремикса с двойной проверкой блокировки;замените первые три строки на:
Map<ActivityIdentifier, Object> locksForDatabase = locks.get(databaseIdentifier);
if (locksForDatabase == null) {
Map<ActivityIdentifier, Object> newHashMap = new HashMap<ActivityIdentifier, Object>();
locksForDatabase = locks.putIfAbsent(databaseIdentifier, newHashMap);
if (locksForDatabase == null) locksForDatabase = newHashMap;
}
В общем случае, когда карта для каждой базы данных уже существует, будет выполнен один одновременный get
.В редких случаях, когда это не так, он будет делать дополнительные, но необходимые new HashMap()
и putIfAbsent
.В очень редком случае этого не происходит, но другой поток также обнаружил, что один из потоков будет выполнять избыточные new HashMap()
и putIfAbsent
.Это не должно быть дорого.
На самом деле, мне приходит в голову, что это ужасная идея, и что вы должны просто соединить два идентификатора вместе, чтобы получить один ключ двойного размера, и использовать его для поискав одном ConcurrentHashMap
.К сожалению, я слишком ленив и тщеславен, чтобы удалить вышесказанное.Считайте этот совет особым призом за чтение этого места.
PS Меня всегда слегка раздражает, когда я вижу экземпляр объекта, который используется только как замок.Я предлагаю назвать их LockGuffins .