Блокировка по значению с отдельной гарантией блокировки - PullRequest
0 голосов
/ 11 июня 2018

Я хотел бы иметь некоторый механизм, который позволит мне получать блокировку для объекта в соответствии с реализацией equals().

Я искал существующее решение.Я нашел Guava Stripped , но проблема в том, что у меня нет гарантии, что для разных значений equals() я получу разные блокировки.

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

Есть ли какое-либо существующее решение для того, чего я хотел бы достичь?Самостоятельно реализовать это может быть довольно сложно, поэтому я подумал о каком-то существующем и протестированном решении.

РЕДАКТИРОВАТЬ: По мере возникновения вопросов я постараюсь объяснить немного больше.У меня есть какая-то транзакция, в которой я обновляю состояние двух объектов.Как только я запускаю эту транзакцию, я не хочу, чтобы какой-либо другой поток запускал любую транзакцию, включающую любой из этих двух объектов, пока я не завершил уже запущенную транзакцию.Однако я хотел бы, чтобы была возможность начать любую другую транзакцию, которая включает другие объекты.Пример:

Объекты: A , A ' (то же значение equals(), что и A , другой экземпляр), B , C

Thread 1 : попытка установить блокировку на A и B

Поток 2 : попытка установить блокировку на A ' и C

Ожидаемый результат: если Поток 1 запущентранзакция, Поток 2 должен ждать до тех пор, пока Поток 1 не завершит свою транзакцию (поскольку A и A ' имеют одинаковое значение equals()).

Ответы [ 2 ]

0 голосов
/ 11 июня 2018

Это решение, которое я реализовал после некоторых размышлений и предложений от @Michael.Может быть, кто-то найдет это полезным или найдет в нем ошибки:

public class LockedExecutionProvider {
    private final HashMap<Long, LockWithUsageCount> locks = new HashMap<>();

    public <T> T executeLocked(Supplier<T> supplier, Long... ids) {
        List<Lock> idsLocks = asList(ids).stream().sorted().map(this::getLock).collect(Collectors.toList());
        idsLocks.forEach(Lock::lock);
        try {
            return supplier.get();
        } finally {
            idsLocks.forEach(Lock::unlock);
            asList(ids).forEach(this::returnLock);
        }
    }

    private synchronized Lock getLock(Long id) {
        LockWithUsageCount lockWithUsageCount = locks.computeIfAbsent(id, i -> new LockWithUsageCount());
        lockWithUsageCount.incrementUsageCount();
        return lockWithUsageCount.getLock();
    }

    private synchronized void returnLock(Long id) {
        LockWithUsageCount lockWithUsageCount = locks.get(id);
        if (0 == lockWithUsageCount.decrementUsageCount()) {
            locks.remove(id);
        }
    }

    private static final class LockWithUsageCount {
        private final Lock lock;
        private int usageCount;

        LockWithUsageCount() {
            this.lock = new ReentrantLock();
            usageCount = 0;
        }

        Lock getLock() {
            return lock;
        }

        void incrementUsageCount() {
            usageCount++;
        }

        int decrementUsageCount() {
            return --usageCount;
        }
    }
}
0 голосов
/ 11 июня 2018

Не строго используя equals, но equals и hashCode (вы все равно должны их переопределять), что не так со следующим?

class LockFactory<T>
{
    private final Map<T, Object> objectToLock = new HashMap<>();

    synchronized Object getLock(T input)
    {
        objectToLock.putIfAbsent(input, new Object());
        return objectToLock.get(input);
    }
}

Вы можете выполнить синхронизацию на Object или верните Lock, если хотите.

...