Разве переменные в Java не должны быть видны во втором потоке? - PullRequest
1 голос
/ 28 марта 2020

У меня есть небольшой пример приложения с двумя потоками A и B. Оба тратят несколько циклов ЦП, прежде чем каждая установка значения для одной изменчивой переменной (A устанавливает значение для x, B устанавливает значение для y), затем B также печатает две переменные. Когда эта игра повторяется несколько раз, то иногда значение x отображается в B, а иногда нет (даже если x и y оба являются изменчивыми). Почему это?

public class Vol2 {

    public static volatile int x = 0;
    public static volatile int y = 0;

    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 50; i++) {

            x = 0;
            y = 0;


            Thread t1 = new Thread(() -> {
                doWork();
                x = 5;
            });

            Thread t2 = new Thread(() -> {
                doWork();
                y = 6;
                System.out.println("x: " + x + ", y: " + y);
            });

            t1.start();
            t2.start();
            t1.join();
            t2.join();

        }
    }

    public static void doWork() {
        int s = 0;
        for (int i = 0; i < 1_000_000; i++) {
            s += i;
        }
    }
}

Ответы [ 3 ]

1 голос
/ 28 марта 2020

Потоки t1 и t2 могут выполняться одновременно. Нет гарантии, что t1 назначит x до того, как t2 присвоит y и прочитает x.

0 голосов
/ 13 апреля 2020

В вашем примере есть тонкие проблемы. Во-первых, doWork может быть совершенно бесполезным, у него нет побочных эффектов и JIT может полностью устранить это. Подумайте об этом: поскольку l oop является чисто локальной вещью, которую никто не видит, зачем вообще это делать?

Тогда у вас как-то неправильное понимание Thread::start, что этот ответ уже объясняет вам. t1.start(); планирует запуск потока, это не значит, что он завершится sh (или даже запустится) до t2.start();.

Тогда, по крайней мере, ИМХО, вы используете не тот инструмент для работы. Вам нужен jcstress , инструмент, разработанный экспертами для такого рода вещей. Вот как ваш код будет выглядеть при его использовании:

@JCStressTest
@State
@Outcome(id = "0, 0", expect = Expect.FORBIDDEN, desc = "can not happen")
@Outcome(id = "0, 6", expect = Expect.ACCEPTABLE, desc = "writerY only")
@Outcome(id = "5, 6", expect = Expect.ACCEPTABLE, desc = "both")
public class TwoThreads {


    volatile int x = 0;
    volatile int y = 0;

    @Actor
    void writerX(II_Result r) {
        x = 5;
    }

    @Actor
    void writerY(II_Result r) {
        y = 6;
        r.r1 = x;
        r.r2 = y;
    }

}

Как выполнить это и что это значит - это упражнение для вас, но главное в том, что выполнение этого даст вам только два Возможные результаты:

5, 6 --> meaning writerX finished its work before writerY

или переведено в ваш код, t1 до t2.

0, 6 --> meaning writerY finished its work before writerX

и, таким образом, x = 5 на writerX было потеряно и никогда не записывалось. Или, переведенный в ваш код, t2 закончен до t1.

0 голосов
/ 28 марта 2020

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

public class Vol2 {

    public static volatile int x = 0;
    public static volatile int y = 0;

    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 50; i++) {

            x = 0;
            y = 0;


            Thread t1 = new Thread(() -> {
                x = 5;
                doWork();
            });

            Thread t2 = new Thread(() -> {
                y = 6;
                doWork();
                System.out.println("x: " + x + ", y: " + y);
            });

            t1.start();
            t2.start();
            t1.join();
            t2.join();

        }
    }

    public static void doWork() {
        int s = 0;
       try {
           Thread.sleep(10);
       } catch (InterruptedException ie){}
    }
}
...