Методы семафора Java не работают должным образом

Я новичок в проблеме производителя-потребителя и семафоре. Следующий код становится тупиковым в условии, что поток 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;
    }
}

Потребительский поток не запускается в тестовом случае, когда производитель получает разрешение... пожалуйста, помогите

Было бы полезно сделать дамп стека процесса, когда он застрял, с первого взгляда я не вижу, где происходит взаимоблокировка (возможно, если один поток умирает между двумя попытками stmts?). Но, как правило, использовать 2 семафора не идеально, один семафор предназначен для защиты одного ресурса от доступа определенного количества процессов. Я подозреваю, что то, что вы хотите сделать, было бы лучше сделать с помощью блокировки и условия (см. пример здесь devdocs.io/openjdk~19/java.base/java/util/concurrent/locks/…‌​)

nomoa 12.04.2023 22:07

Почему вы думаете, что существует тупик? Когда я запускаю вашу программу, нет тупика. Программа завершается нормально...

Solomon Slow 12.04.2023 22:49

...После печати одной строки вывода. Если я изменю sleep(10) на sleep(100), он напечатает много строк вывода. Возможно, десяти миллисекунд сна недостаточно, чтобы позволить потокам начать работу и выполнить столько работы, сколько вы от них ожидали.

Solomon Slow 12.04.2023 22:51

@SolomonSlow возникает тупиковая ситуация ... запускайте программу более одного раза, есть случай, когда программа никогда не завершается ... трассировка стека показывает, что это происходит, когда производитель пытается получить разрешение в semProd.acquire();

Chetan Sharma 417 12.04.2023 22:56

@SolomonSlow, лол, после перехода в режим сна (100) возникает взаимоблокировка из-за потребителя в semCon.acquire () ... Может быть, причина в том, что я делаю флаг «проверить» после Thread.sleep () как ложный, и поток ждет навсегда, чтобы получить разрешение ...

Chetan Sharma 417 12.04.2023 23:00

@nomoa да, это здорово, используя блокировку и условие .. Я просто хотел поэкспериментировать с семафором

Chetan Sharma 417 12.04.2023 23:06

если производитель или потребитель никогда не входят в цикл while, вы получите взаимоблокировку, это возможно, если для проверки установлено значение false до достижения цикла while. Вы должны установить флаг проверки в false только после того, как вы уверены, что производитель что-то произвел или что потребитель фактически вошел в цикл.

nomoa 12.04.2023 23:29

@nomoa да, вы правы, но код застревает в semCon.acquire(), как показывает трассировка стека в окне DEBUG => Thread [c1] (Выполняется) .. плюс это после того, как цикл while вошел .. даже флаг ложно, разрешение должно быть доступно, но это не

Chetan Sharma 417 12.04.2023 23:34

Точно так же, когда цикл заканчивается, например. сразу после semProd.release(); если p1 зациклится дважды (первую итерацию он произведет, вторую будет ждать semProd.acquire()), тогда для проверки установлено значение false, c1 завершается, но p1 остается зависшим.

nomoa 13.04.2023 00:14
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
9
76
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Расширенный комментарий, а не ответ.

Номоа сказал: «Если производитель или потребитель никогда не войдут в цикл 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.

Arfur Narf 13.04.2023 00:48

@chetansharma, вы должны принять ответ nomoa вместо этого. Я не хотел, чтобы это был ответ. Я только пытался расширить то, что nomoa сказал в комментарии.

Solomon Slow 13.04.2023 05:24
Ответ принят как подходящий

Ваша проблема, я думаю, в отсутствии синхронизации вокруг флага 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 .

Другие вопросы по теме