Потоки не заканчиваются; застрял в петле - PullRequest
0 голосов
/ 15 апреля 2020

У меня есть 4 потока, каждый из которых выполняет свой метод, я могу получить желаемый вывод, однако потоки не завершаются или не могут выйти из l oop после завершения задания.

Может ли кто-нибудь помочь мне с вашим мнением.

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

Когда один поток сигнализирует другому, не выполняет ли выполняющиеся потоки перепроверку условия некоторое время l oop, так как я устанавливаю флаг в ложь, но это не имеет никакого эффекта.

    private int n;
    Lock lock = new ReentrantLock();
    Condition executeFizz;
    Condition executeBuzz;
    Condition executeFizzBuzz;
    Condition executeNumber;
    volatile private boolean flag;   

    public FizzyBuzz(int n) {
        this.n = n;
        this.executeFizz = lock.newCondition();
        this.executeBuzz = lock.newCondition();
        this.executeFizzBuzz = lock.newCondition();
        this.executeNumber = lock.newCondition();
        this.flag = true;
    }

    public  void fizz()   {
        lock.lock();
        while(true) {
        try {           
            executeFizz.await();
            if(!flag) break;
        } catch (InterruptedException e) {
        }
        System.out.println("fizz");
        executeNumber.signal();
        }

    }

    public void buzz()  {
        lock.lock();
        while(true) {
        try {
            executeBuzz.await();
            if(!flag) break;
        } catch (InterruptedException e) {
        }
        System.out.println("buzz");
        executeNumber.signal();

        }

    }

    public void fizzbuzz()   {
        lock.lock();
        while(true) {
        try {
            executeFizzBuzz.await();
            if(!flag) break;
        } catch (InterruptedException e) {
        }
        System.out.println("fizzbuzz");
        executeNumber.signal();
        }

    }

    public  void number() throws InterruptedException  {
        lock.lock();
        for(int i=1;i<=n;i++)
        {       
            if(i%3==0&&i%5==0)
                {
                    executeFizzBuzz.signal();
                    executeNumber.await();
                }
            else if(i%3==0)
                {

                    executeFizz.signal();
                    executeNumber.await();
                }
                else if(i%5==0)
                {
                    executeBuzz.signal();
                    executeNumber.await();
                }
                else
                {
                    System.out.println(i);
                }   
        }
        flag = false;
        executeFizzBuzz.signal();
        executeFizz.signal();
        executeBuzz.signal();

1 Ответ

1 голос
/ 15 апреля 2020

Может быть, это не делает то, о чем вы думали:

while (flag) {
    ...
}

То, что l oop будет не завершится всякий раз, когда flag станет ложным. Он заканчивается, когда он проверяет flag и находит его ложным. Это происходит только один раз каждый раз вокруг l oop. Вместо этого вы могли бы написать l oop как этот, и он будет вести себя точно так же:

while (true) {
    if (! flag) break;
    ...
}

Так что, если у вас есть это,

while (flag) {
    ...executeFizz.await()...
}

И если flag изменяется с истинного на ложное, когда поток застревает в вызове await(), тогда l oop не прекратит работу до тех пор, пока после вызова await() не вернется ...

... этого никогда не произойдет в вашем примере, потому что после того, как for l oop сосчитал до n, он устанавливает flag=false;, но не сигнализирует другим ожидающие потоки.


@ SolomonSlow Я пытался сигнализировать все 3, но безуспешно после добавления условия if, в конце кода я включил этот код ...

Полагаю, это потому, что в вашей программе нет вызова lock.unlock(). Вот что, вероятно, происходит:

  1. Три «рабочих» потока все находятся в вызовах executeXxxxx.await() (каждый ожидает свою собственную переменную условия.)

  2. «Основной» поток достигает конца l oop (в настоящее время он является владельцем lock,) и сигнализирует о каждой из executeXxxxxx переменных состояния.

  3. «Основной» поток заканчивается, но lock все еще заблокирован. Все три «рабочих» потока теперь освобождены от ожидания соответствующих условий, но их вызовы await() не могут вернуться, пока блокировка не станет доступной.

  4. Блокировка никогда не станет доступно, потому что нет работающего потока, который может разблокировать его.

Вам нужно, чтобы каждый поток вызывал lock.unlock() до его завершения.

...