Мертвая блокировка потока

У меня есть 2 потока. Один поток печатает нечетные числа, и второй поток печатает четные числа. Теперь, я должен выполнить потоки альтернативно так, чтобы я мог произвести 1,2,3,4,5,6.....

Я записал программу для этого, и это приводит к мертвой блокировке. Кто-то может объяснить, какова проблема с кодом и как исправить его?

class BooleanObject {
boolean flag;
BooleanObject(boolean flag) {
    this.flag = flag;
}
}
class EvenThread extends Thread {
Object lock;
BooleanObject flagObj;
EvenThread(Object o, BooleanObject flag) {
    lock = o;
    this.flagObj = flag;
}
public void run() {
    for (int i=2;i<100;i+=2) {
        synchronized(lock) {
            if (flagObj.flag == false) {
                flagObj.flag = true;
                lock.notify();
            }
            else {
                try {
                    while (flagObj.flag == true) {
                        lock.wait();
                    }
                }
                catch (InterruptedException e) {

                }
            }
            System.out.println(i);
        }
    }
}
}

class OddThread extends Thread {
Object lock;
BooleanObject flagObj;
OddThread(Object o, BooleanObject flag) {
    lock = o;
    this.flagObj = flag;
}
public void run() {
    for (int i=1;i<100;i+=2) {
        synchronized(lock) {
            if (flagObj.flag == true) {
                flagObj.flag = false;
                lock.notify();
            }

            else {
                try {
                    while(flagObj.flag == false) {
                        lock.wait();
                    }
                }
                catch (InterruptedException e) {

                }
            }
            System.out.println(i);
        }
    }
}
}

public class EvenOddThreads {
public static void main(String[] args) {
    Object obj = new Object();
    BooleanObject flagObj = new BooleanObject(true);
    EvenThread et = new EvenThread(obj,flagObj);
    OddThread ot = new OddThread(obj,flagObj);

    et.setName("even thread");
    ot.setName("odd thread");

    et.start();
    ot.start();
}
}
7
задан erickson 17 February 2010 в 19:31
поделиться

3 ответа

Проблема заключается в автоматическом блокировании. Если вы измените флаг с true на false или наоборот, вы фактически получаете совершенно новый объект Boolean . То есть эта строка:

flag = false;

Эквивалентна:

flag = new Boolean(false);

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

Поскольку объект Boolean неизменяем, вам необходимо изменить свой флаг, чтобы использовать какой-либо другой изменяемый объект, который может изменять значения на месте без создания новых объектов. Это или оба класса ссылаются на общую (возможно, глобальную) переменную.

Как предлагает @erickson, вы можете использовать AtomicBoolean , который является изменяемым. Другой сложный способ сделать это - изменить флаг на:

boolean[] flag = new boolean[1];

И затем везде использовать флаг [0] . Тогда оба потока смогут изменить flag [0] , всегда ссылаясь на один и тот же объект массива boolean [] . У вас не было бы проблемы с автобоксом.

...

Также рекомендуется заключить любой вызов wait () в цикл. wait () может быть предметом ложного пробуждения, когда вызов возвращается, даже если на самом деле никто не вызвал notify () . Чтобы обойти эту проблему, вы всегда должны проверять состояние охраны после пробуждения, чтобы убедиться, что пробуждение не является ложным.

while (flag == true) {
    lock.wait();
}

Обновление

Я внес изменения на основе ваших предложений выше; но я не получаю ожидаемого результата. Я вставлю модифицированный код выше. Вот результат, который я получаю 1 2 4 3 5 6 8 7 9 10 11 13 12 15 17 14 ....

Когда вы в конечном итоге ждете, когда вы просыпаетесь, вы не переключаете флаг и уведомить другой поток. Я советую немного реорганизовать ваш код, чтобы он выглядел как «подождите; напечатайте; уведомите».Примерно так:

synchronized (lock) {
    while (flagObj.flag == false) {
        lock.wait();
    }

    System.out.println(i);

    flagObj.flag = false;
    lock.notify();
}
9
ответ дан 6 December 2019 в 19:36
поделиться

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

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

Чтобы эта работа работала, как задумано, создайте изменяемую логическую оболочку ( AtomicBoolean выполнит эту работу, хотя вы не будете использовать ее свойства параллелизма в этом приложении) и передайте эту оболочку каждому потоку. Каждый поток будет изменять этот единственный объект, а не назначать новый объект своей собственной переменной.

4
ответ дан 6 December 2019 в 19:36
поделиться

На самом деле у вас здесь две проблемы.

1) Первый - this

if (flag == true) {
    flag = false;
    lock.notify();
}

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

Попробуйте что-нибудь вроде

class Monitor
{
    public static volatile boolean flag;
}

А затем используйте Monitor.flag в каждом потоке.

2) Вторая проблема (после исправления 1-го) заключается в том, что каждый поток должен иметь this

synchronized(lock)
{
    lock.notify();
}

в конце после цикла, потому что в противном случае один поток будет wait (), а другой поток уже выполнен.

0
ответ дан 6 December 2019 в 19:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: