Блокировка блокировки, принадлежащая другому потоку java

У меня есть LockManager, который управляет блокировками нескольких потоков. Иногда потоки - плохие парни, и я должен их убить и попросить LockManager выпустить все свои блокировки. Однако, поскольку я использую ReentrantLock в java, это невозможно, я не могу разблокировать блокировку, принадлежащую другому потоку.

Я вынужден использовать Locks (не могу использовать семафоры, это точка домашней работы). Есть ли какая-либо реализация Java Lock, которая позволяет мне разблокировать блокировки, принадлежащие другим потокам?

До сих пор варианты, которые я рассматривал, следующие:

  • повторное внедрение ReentrantLock таким образом, чтобы это можно было сделать
  • Сделать какое-то сопоставление между Семафорами и ReentrantLocks

Дополнительные источники, которые могут оказаться полезными:

Ответы

Ответ 1

Можно ли использовать собственный Lock? Здесь класс, который полностью проксирует Lock, но когда ему говорят, чтобы заставить разблокировать, он просто заменяет блокировку, которую он проксирует с помощью новой. Это должно иметь эффект, который вы хотите. К сожалению, это все еще не касается замков, которые остаются болтающимися, но теперь это становится чьей-то другой проблемой. Теперь ваши блокировки магически разблокированы.

static class LockProxy<L extends Lock> implements Lock {

    // The actual lock.
    private volatile Lock lock;

    public LockProxy(L lock) {
        // Trap the lock we are proxying.
        this.lock = lock;
    }

    @Override
    public void lock() {
        // Proxy it.
        lock.lock();
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        // Proxy it.
        lock.lockInterruptibly();
    }

    @Override
    public boolean tryLock() {
        // Proxy it.
        return lock.tryLock();
    }

    @Override
    public boolean tryLock(long l, TimeUnit tu) throws InterruptedException {
        // Proxy it.
        return lock.tryLock(l, tu);
    }

    @Override
    public void unlock() {
        // Proxy it.
        lock.unlock();
    }

    @Override
    public Condition newCondition() {
        // Proxy it.
        return lock.newCondition();
    }

    // Extra functionality to unlock from any thread.
    public void forceUnlock() {
        // Actually just replace the perhaps locked lock with a new one.
        // Kinda like a clone. I expect a neater way is around somewhere.
        if (lock instanceof ReentrantLock) {
            lock = new ReentrantLock();
        } else {
            throw new UnsupportedOperationException(
                "Cannot force unlock of lock type "
                    + lock.getClass().getSimpleName());
        }
    }
}

Ответ 2

Вы обнаружили основную причину, почему общая мудрость гласит: "Не убивайте нити!"

Замки являются лишь одним из потенциальных утечек ресурсов, которые могут произойти, если вы принудительно уничтожаете поток. Рассмотрите открытые файлы и сокеты и т.д.

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

Лучший способ справиться с этой ситуацией - попросить поток уйти. Добавьте метод "stop()" к объекту, связанному с потоком (у вас есть объект для каждого потока, не так ли?), Который устанавливает флаг и регулярно проверяет этот флажок и выходит, если он установлен.

Если ваши потоки ошибочны, что мешает им проверять флаг остановки, тогда правильный подход заключается в том, чтобы исправить код, чтобы он не ошибался.

Ответ 3

Как указано в комментариях, убийство тем не является хорошей практикой. Большинство сред делают все возможное, чтобы прерывать потоки в рабочих очередях, но они будут иметь эффект только в том случае, если исполняемый код проверяет флаг прерывания, либо вызывая Thread.isInterrupted() либо вызывая прерывистый ввод-вывод или метод блокировки.

Если вам действительно нужна концепция уничтожения выполнения кода, взгляните на класс Process. Вы можете создать Process, вызвав Runtime.exec() или используя ProcessBuilder. Вызов Process.destroyForcibly() принудительно завершит работающий процесс.

Ответ 4

Я сделал это с AtomicReference, который получает ноль очков за элегантность, но я не знаю другого пути.

class PseudoLock {

    private final AtomicReference<Boolean> mylock = new AtomicReference<>(Boolean.FALSE);


    boolean trylock() {
        return mylock.compareAndSet(Boolean.FALSE, Boolean.TRUE);
    }

    void unlock() {
        boolean done = mylock.compareAndSet(Boolean.TRUE, Boolean.FALSE);
        if (!done) {
            throw new IllegalStateException("Cannot unlock an unlocked thread");
        }
    }
}

"" ""

Ответ 5

Почему бы вам просто не обернуть код вашего потока вокруг следующего:

ReentrantLock lock = ... obtain your lock somehow ...
lock.lock();
try {
  ... the "bad boy" code here ...
} finally {
  lock.unlock();
}

Затем, когда ваш поток заканчивается (либо путем обычной обработки, либо путем исключения исключения из вашего "kill" ), он освобождает блокировку.

На самом деле Oracle рекомендует использовать ReentrantLock: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantLock.html