Блокировка блокировки, принадлежащая другому потоку 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