Как использовать wait () и notifyAll () для запуска потока один за другим?

Я пишу класс House, в нем четыре синхронизированных метода. Я пишу четыре потока и хочу, чтобы они запускались один за другим. но только первый и второй бежали, а другой нет?

public class House {

    private boolean hasFoundation = false;
    private boolean hasFrame = false;
    private boolean hasWall = false;
    private boolean hasRoof = false;

    public synchronized void buildFoundation() {
        hasFoundation = true;
        System.out.println("foundation Ok");
        notifyAll();
    }

    public synchronized void buildFrame() throws InterruptedException {
        if (!hasFoundation) {
            wait();
        } else {
            hasFrame = true;
            System.out.println("frame ok");
            notifyAll();
        }
    }

    public synchronized void buildWall() throws InterruptedException {
        if (!hasFrame) {
            wait();
        } else {
            hasWall = true;
            System.out.println("wall ok");
            notifyAll();
        }
    }

    public synchronized void buildRoof() throws InterruptedException {
        if (!hasWall) {
            wait();
        } else {
            hasRoof = true;
            System.out.println("roof ok");
            notifyAll();
        }
    }
}

public class BuildAHouse {

    public static void main(String[] args) {
        House house = new House();

        ExecutorService exec = Executors.newCachedThreadPool();

        exec.execute(new FoundationTeam(house));
        exec.execute(new WallTeam(house));
        exec.execute(new RoofTeam(house));

        exec.execute(new FrameTeam(house));

        exec.shutdown();
    }
}

при запуске main () результат будет просто: foundation ok frame ok

два других потока не запустились! Зачем?

класс команды вроде этого:

public class FoundationTeam implements Runnable {

    private House house;

    public FoundationTeam(House house) {
        this.house = house;
    }

    @Override
    public void run() {
        house.buildFoundation();
    }

}

это просто демонстрация, я хочу знать, как использовать wait () и notifyAll ().

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

при вызове wait () этот объект не будет освобожден? а другие потоки не могут вызывать другие синхронизированные методы?


person Oolong    schedule 29.09.2016    source источник
comment
Я не знаю, что вы в этих ...Team классах, но вы, вероятно, выполняете метод только один раз. Потоки останавливаются, и когда они получают уведомление, они продолжают выход из метода, потому что у них никогда не будет возможности войти в это предложение else за исключением FrameTeam   -  person kevto    schedule 29.09.2016
comment
Я не думаю, что вам действительно нужны wait() и notifyAll(): у вас уже есть ExecutorService, это абстракция синхронизации более высокого уровня. См. stackoverflow.com/q/2153663/5521491   -  person Adamovskiy    schedule 29.09.2016
comment
Я не думаю, что это правильный вариант использования. Чего бы вы ни пытались достичь, этого легко добиться. Пример уведомления об ожидании: 1. Потребитель-производитель 2. Четный нечетный принтер, использующий 2 потока 3. Распечатайте числа, используя 3 потока, последовательно   -  person cody123    schedule 29.09.2016


Ответы (3)


Если ваш метод действительно wait (), он не будет запускать ничего в блоке else

Примечание: wait () может ложно проснуться, рекомендуется цикл while.

person Peter Lawrey    schedule 29.09.2016
comment
вызов метода wait (), другой вызов notifyAll () не может разбудить? - person Oolong; 29.09.2016
comment
@Oolong Если вы ждете (), вы больше ничего не делаете. - person Peter Lawrey; 29.09.2016

два других потока не запустились! Зачем?

Как упоминал @Peter, и вы уже догадались, вам нужен while(!boolean) цикл вокруг ваших циклов ожидания. Это необходимо по нескольким причинам.

Как упоминает Питер, wait() может вернуться из-за ложного пробуждения вместо правильного notify() вызова. Вам нужно убедиться, что условие, которого вы ждете, действительно установлено, а затем выполнить цикл и снова вызвать wait(), если это не так.

Однако в вашем случае речь идет не столько о ложных пробуждениях, сколько о том, как написана ваша программа. Поскольку у вас есть один объект synchronized (объект House), когда вы вызываете notifyAll(), все потоки команд пробуждаются. Когда вызывается метод buildFoundation(), он устанавливает для hasFoundation значение true и пробуждает все команды. Но на самом деле только команда разработки может начать работу - другим командам нужно выполнить цикл и еще немного подождать, пока их логическое значение не будет установлено в значение true.

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

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

person Gray    schedule 30.09.2016

Это работает!

public synchronized void buildWall() throws InterruptedException {
    while (!hasFrame) {
        wait();
    }
    hasWall = true;
    System.out.println("wall ok");
    notifyAll();
}

добавьте "while ()", но я уже не знаю почему!

person Oolong    schedule 29.09.2016
comment
Потому что вам нужно ждать, пока hasFrame не станет истинным. Функция wait не знает, чего она ждет, и не знает, произошло ли то, чего она ждет. Так что это ваша ответственность. - person David Schwartz; 30.09.2016