Вопрос по volatile, java, multithreading, static – Почему volatile не работает должным образом

2

Сегодня я создавал одну тайм-аут работу, используяTimerTask но попал в новую проблему, где у меня естьstatic volatile boolean переменнаяflag, Насколько я понимаю, как только значение этой переменной изменяется, она уведомляется всеми работающимиthread, Но когда я запустил эту программу, я получил вывод ниже, что не приемлемо.

O/P:

--------------
--------------
DD
BB
Exiting process..
CC

Я ожидаю, что мой последний отпечаток должен бытьExiting process.. Почему это странное поведение?

Мой код:

public class TimeOutSort {

    static volatile boolean flag = false;

    public static void main(String[] args) {

        Timer timer = new Timer();
        timer.schedule(new TimerTask() {

            @Override
            public void run() {

                flag = true;
                System.out.println("Exiting process..");
                // System.exit(0);
            }
        }, 10 * 200);

        new Thread(new Runnable() {

            @Override
            public void run() {
                while (!flag)
                    System.out.println("BB");

            }
        }).start();

        new Thread(new Runnable() {

            @Override
            public void run() {
                while (!flag)
                    System.out.println("CC");

            }
        }).start();

        new Thread(new Runnable() {

            @Override
            public void run() {
                while (!flag)
                    System.out.println("DD");

            }
        }).start();
    }

}

Edit: Как я могу достичь этого?

@PeterLawrey, может быть, я могу понять, в чем проблема amicngh
Потому что последовательность выполнения потоков не гарантирована !! kosa
Я не говорю о секвенировании, мне просто любопытно, как скоро мой флаг станет истинным, почему выполняется оператор while в цикле? amicngh
Вы говорите, чтоvolatile не работает или вы используете его неправильно? ;) Peter Lawrey
Вы не должны действительно изменять содержание вашего вопроса после того, как на него был дан ответ при редактировании. Вы попросили объяснения, вы наблюдали за поведением, которое вы наблюдали, и вы получили совершенно хорошие ответы на это; несправедливо перемещать стойку ворот. millimoose

Ваш Ответ

6   ответов
1

в выходных данных вашего примера - нить, которая печатает"CC" был приостановлен (где-то) & quot;between& Quot; линииwhile (!flag) а такжеSystem.out.println(), Это означает, что после того, как он проснется,println() исполняетbefore следующая проверка флага. (Он также не проснется только из-за того, что вы изменили значение флага, а из-за того, что какой-то другой поток блокирует или использует свой временной интервал.)

3

volatile в значительной степени это означает, что каждый раз, когда поток обращается к переменной, он должен обеспечивать использование версии, видимой для каждого потока (т. е. нет кэширования для каждого потока).

этоdoesn't заставить нить CC-печати на самом деле запуститьimmediately послеflag был установлен наtrue, Вполне возможно (особенно на одноядерном компьютере), что один поток устанавливает флагand печатает сообщениеbefore у нити CC-печати даже был шанс побежать.

Также: обратите внимание, что печать наSystem.out включает в себя приобретение блокировки (где-то внутриprintln() вызов), который можетmodify многопоточное поведение тестового кода.

0

public class TimeOutSort {

static volatile boolean flag = false;

public static void main(String[] args) {

    Timer timer = new Timer();
    timer.schedule(new TimerTask() {

        @Override
        public void run() {

            synchronized(flag){
                 flag = true;
                 notifyAll();
            }


        }
    }, 10 * 200);

    new Thread(new Runnable() {

        @Override
        public void run() {

            synchronized(flag){
                if(!flag)
                {
                    wait();
                }
                System.out.println("BB");

            }


        }
    }).start();

    new Thread(new Runnable() {

        @Override
        public void run() {

              synchronized(flag){
                if(!flag)
                {
                    wait();
                }
                System.out.println("CC");

            }
        }
    }).start();

    new Thread(new Runnable() {

        @Override
        public void run() {

              synchronized(flag){
                if(!flag)
                {
                    wait();
                }
                System.out.println("DD");

            }


        }
    }).start();
}

}

1

некоторые из ваших потоков не остановились бы). Речь идет о порядке выполнения инструкций в разных потоках, и это является случайным (зависит от планирования ОС), если вы явно не синхронизируете циклы на промежуточных этапах.

@thinksteep Я имел в виду & quot; синхронизацию & quot; в общем смысле - использование защелок, барьеров или любого другого механизма, который позволяет одному потоку контролировать выполнение другого, а не простое взаимное исключение (использование блока "synchronized" внутри цикла), которое, как вы правильно указали, не будет " решить вопрос заказа здесь.
Случайность все еще существует независимо от синхронизации. Синхронизация гарантирует один поток за раз, но не гарантирует, что поток A выполняется сначала, а затем поток B и т. Д.,
1

CC & quot; случалось, что процессорное время не получалось до тех пор, пока ваш поток не напечатал & quot; Exiting process ... & quot; напечатал это. Это ожидаемое поведение.

Как я могу добиться этого поведения? amicngh
использованиеThread.join ждать завершения всех остальных потоков.
3

thread BB: while (!flag) // as flag is false
thread Main:   flag = true;
thread Main:   System.out.println("Exiting process..");
thread BB:     System.out.println("BB");

My expectation is my last print should be Exiting process..

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

Но тогда они будут выполняться по порядку. amicngh
@ amicngh А? Нет, я имел в виду синхронизацию в общем смысле "зная, что, черт возьми, ты делаешь при использовании потоков", а не смысл "взаимного исключения" этот выбор ключевого слова Java для этого подразумевает.
Тогда как я могу достичь этого? amicngh
@amicngh Правильная синхронизация. В этом случае, имеяTimerTask подождите, пока остальные не закончат, используяjoin(), Я рекомендую нажатьan operating systems textbook а также, где этот предмет имеет тенденцию быть покрытым.

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