Я новичок в проблеме производителя-потребителя и семафоре. Следующий код становится тупиковым в условии, что поток Producer зависает, когда он снова получает разрешение сразу после добавления в очередь.
Только это в том случае, когда программа заблокирована.
public class UsingSemaphore {
volatile static boolean check = true;
public static void main(String args[]) throws InterruptedException {
Semaphore semCon = new Semaphore(0);
Semaphore semProd = new Semaphore(1);
Queue<Integer> q = new LinkedList<>();
// Producer lambda
Runnable producer = () -> {
while (check) {
try {
semProd.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Random rand = new Random();
q.add(rand.nextInt(10));
}finally {
semCon.release();
}
}
};
//Consumer lambda
Runnable consumer = () -> {
while (check) {
try {
semCon.acquire();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
System.out.println("Consumer item " + q.remove());
}finally {
semProd.release();
}
}
};
ThreadGroup pg = new ThreadGroup("PG");
ThreadGroup cg = new ThreadGroup("CG");
Thread p1 = new Thread(pg, producer, "p1");
Thread c1 = new Thread(cg, consumer, "c1");
p1.start();
c1.start();
Thread.sleep(10);
check = false;
}
}
Потребительский поток не запускается в тестовом случае, когда производитель получает разрешение... пожалуйста, помогите
Почему вы думаете, что существует тупик? Когда я запускаю вашу программу, нет тупика. Программа завершается нормально...
...После печати одной строки вывода. Если я изменю sleep(10) на sleep(100), он напечатает много строк вывода. Возможно, десяти миллисекунд сна недостаточно, чтобы позволить потокам начать работу и выполнить столько работы, сколько вы от них ожидали.
@SolomonSlow возникает тупиковая ситуация ... запускайте программу более одного раза, есть случай, когда программа никогда не завершается ... трассировка стека показывает, что это происходит, когда производитель пытается получить разрешение в semProd.acquire();
@SolomonSlow, лол, после перехода в режим сна (100) возникает взаимоблокировка из-за потребителя в semCon.acquire () ... Может быть, причина в том, что я делаю флаг «проверить» после Thread.sleep () как ложный, и поток ждет навсегда, чтобы получить разрешение ...
@nomoa да, это здорово, используя блокировку и условие .. Я просто хотел поэкспериментировать с семафором
если производитель или потребитель никогда не входят в цикл while, вы получите взаимоблокировку, это возможно, если для проверки установлено значение false до достижения цикла while. Вы должны установить флаг проверки в false только после того, как вы уверены, что производитель что-то произвел или что потребитель фактически вошел в цикл.
@nomoa да, вы правы, но код застревает в semCon.acquire(), как показывает трассировка стека в окне DEBUG => Thread [c1] (Выполняется) .. плюс это после того, как цикл while вошел .. даже флаг ложно, разрешение должно быть доступно, но это не
Точно так же, когда цикл заканчивается, например. сразу после semProd.release(); если p1 зациклится дважды (первую итерацию он произведет, вторую будет ждать semProd.acquire()), тогда для проверки установлено значение false, c1 завершается, но p1 остается зависшим.




Расширенный комментарий, а не ответ.
Номоа сказал: «Если производитель или потребитель никогда не войдут в цикл while, вы получите тупик ...»
Вы ответили: «Да, вы правы, но код зависает на semCon.acquire()».
Конечно. Вот как это может произойти:
Producer Consumer Main
-------------- -------------- -----------
test flag
(it's true)
flag=false
test flag
(it's false)
return
semCon.acquire()
Семафор semCon был инициализирован нулем, и для него никогда не выдается никаких разрешений. Потребитель зависает.
Чтобы производитель зависал при запуске, это немного хитрее, но может случиться и так:
Producer Consumer Main
-------------- -------------- -----------
test flag
(it's true)
semProd.acquire()
semCon.release()
test flag
(it's true)
flag=false
test flag
(it's false)
return
semProd.acquire()
Чем дольше вы делаете основной поток sleep до того, как он установится check=false, тем меньше вероятность того, что может произойти любой из этих двух сценариев.
И вы можете закрыть дыры, прервав потоки (и написав правильные обработчики исключений, а не бесполезные шаблонные, которые просто продолжают выполняться) после установки «проверки» в false.
@chetansharma, вы должны принять ответ nomoa вместо этого. Я не хотел, чтобы это был ответ. Я только пытался расширить то, что nomoa сказал в комментарии.
Ваша проблема, я думаю, в отсутствии синхронизации вокруг флага check, который позволяет вашему циклу начинаться или заканчиваться.
Например, возможно, что производитель запускает цикл и ждет semProd.acquire() (на второй итерации), но потребитель так и не запустился, потому что флаг check был оценен как ложный.
Когда циклы заканчиваются, и потребитель, и производитель должны завершиться в нужное время, иначе может возникнуть взаимоблокировка, если учесть последовательность событий:
C1: semProd.release()
P1: semProd.acquire() -> OK
P1: q.add()
P1: semCon.release()
P1: while(check) -> IN
P1: semProd.acquire() -> wait
MAIN: check <- false
C1: while(check) -> OUT
C1: stops
P1: deadlocked
То же самое применимо и наоборот, если C1 пытается получить semCon .
Было бы полезно сделать дамп стека процесса, когда он застрял, с первого взгляда я не вижу, где происходит взаимоблокировка (возможно, если один поток умирает между двумя попытками stmts?). Но, как правило, использовать 2 семафора не идеально, один семафор предназначен для защиты одного ресурса от доступа определенного количества процессов. Я подозреваю, что то, что вы хотите сделать, было бы лучше сделать с помощью блокировки и условия (см. пример здесь devdocs.io/openjdk~19/java.base/java/util/concurrent/locks/…)