Можно ли приобрести блокировку одним методом и снять ее другим способом? - PullRequest
1 голос
/ 05 мая 2011

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

Например, предположим, что есть два потока, поток 1 и поток 2. Каждый поток вызывает методы с одного по три.

Таким образом, вы что-то получаете в порядке вызованапример:

T1-M1
T1-M2
T2-M1
T1-M3
T2-M2
T2-M3

Однако я хотел бы, чтобы T1-M1 устанавливал какую-то блокировку, которая будет блокировать T2, поэтому порядок будет следующим:

T1-M1 - get lock
        T2-M1 - blocked
T1-M2
T1-M3 - release lock

T2-M1 - no longer blocked - gets lock
T2-M2
T2-M3

Возможно ли сделать это в Java без редактирования вызывающего метода из фреймворка?

Ответы [ 4 ]

6 голосов
/ 05 мая 2011

Вы не можете сделать это с простыми функциями, предоставляемыми ключевым словом synchronized: его блокировка должна быть снята тем же способом (даже блоком), в котором она была получена.

Вы можете использовать инструменты в java.util.concurrent.locks, однако.

Это ReentrantLock класс - это в значительной степени ограничение механизма synchronized.

Однако помните, что такой подход опасен. Что если поток вызывает M1 и M2, но никогда не вызывает M3? Например, в некоторых кодах между вызовами к M2 и M3 может возникнуть исключение.

1 голос
/ 05 мая 2011

Вы не можете снять внутреннюю блокировку с другого метода, но вы можете освободить jucLock из другого метода.Например: (отредактировано на основе ответа Питера Лори, спасибо)

public boolean tryAcquireTimedLock(){
    return lock.tryLock(30, TimeUnit.SECONDS);
}

public void releaseLock(){
   lock.unlock();
}

Здесь предлагается удобный метод прерывистого получения блокировки с таймаутом.

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

public void doSomeWork(){
    if(tryAcquireTimedLock()){

         otherMethodWork();

         finalMethodWork();
    }
}


public void otherMethodWork(){
   //do something that may cause an Exception
}

public void finalMethodWork(){
   //finish work now release lock
    releaseLock();
}

А что, если otherMethodWork() выдает RuntimeException?Ваш код теперь никогда не сможет вызывать releaseLock(), и дальнейший прогресс невозможен.

Именно поэтому ему предписано использовать стиль

lock.lock();
try{
  //work
}finally{
  lock.unlock();
}

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

0 голосов
/ 05 мая 2011

Просто дополнение к другим постам, все 3 метода должны совместно использовать одну и ту же блокировку, общий ресурс здесь является пакетом из 3 методов, вы не хотите, чтобы T1 получал блокировку для M1 и M2, когда T2 уже имеет замок на М3.

0 голосов
/ 05 мая 2011

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

...