Синхронизация разделяемой памяти (int) с java-потоками

В нашем приложении (написанном на Java для Android) у нас есть производный класс Thread, который содержит поле int:

public class MyThread extends Thread {

    public int myValue = 0;

    public void doWork() {

        while (true) {
            System.out.println(myValue);
        }
    }
}

// On some other thread
myThread.myValue = 42;

Класс MyThread только читает из поля int, в то время как я хочу, чтобы другой поток записывал в него (как в примере выше).

Насколько я знаю, примитивные типы, такие как int в Java, читаются/записываются атомарно.

Должен ли я защитить доступ к этому полю int (используя ключевое слово synchronized)?

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

Цитата: Без явной связи вы не можете гарантировать, какие записи будут видны другим потокам.

Должен ли я защитить доступ к этому полю или другой поток может обновить его (атомарно) без каких-либо необходимых изменений?


person lysergic-acid    schedule 31.10.2013    source источник
comment
В вашем простом примере вы понимаете правильно. Проблема возникает, когда поток хочет принять решение на основе этого значения. например. if (value == 42). Затем он входит в тело этого if, но value было изменено на что-то другое. Пока вы знаете об этой возможности... Я говорю это, потому что в этих простых примерах, как правило, упускаются многие сложности многопоточности.   -  person Jonathon Reinhart    schedule 31.10.2013
comment
Это противоречит другим ответам, представленным здесь (например, следует использовать ключевое слово volatile)   -  person lysergic-acid    schedule 31.10.2013
comment
Комментарий Джонатона верен и не противоречит МОЕМУ ответу.   -  person Dawood ibn Kareem    schedule 31.10.2013
comment
Извините, я хотел включить это требование. Я комментировал оба сразу и запутался.   -  person Jonathon Reinhart    schedule 31.10.2013


Ответы (3)


Что вам нужно сделать, так это объявить его как volatile. См. http://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html.

person Dawood ibn Kareem    schedule 31.10.2013
comment
К вашему сведению, то же самое относится и к C/C++ (откуда взялось это ключевое слово) - person Paul Draper; 31.10.2013
comment
Можно уточнить, для чего это нужно? теперь код не будет работать так, как должен? - person lysergic-acid; 31.10.2013
comment
Вы должны посмотреть, что делает ключевое слово volatile. По сути, он говорит компилятору не генерировать код, который запоминает это значение, а вместо этого всегда извлекать его из памяти. - person Jonathon Reinhart; 31.10.2013
comment
Код МОЖЕТ НЕ работать так, как вы его написали. JVM может предположить, что переменная не изменится в цикле, и кэшировать ее. Объявив его как volatile, вы сообщаете JVM, что это не кэшируется. Или ваш код МОЖЕТ работать нормально, но вы не должны рисковать. - person Dawood ibn Kareem; 31.10.2013
comment
@ user2511414 Это правда, но ОП специально сказал, что чтение происходит в одном потоке, а запись - в другом. - person Dawood ibn Kareem; 31.10.2013
comment
Если поле не является изменчивым, значение поля может быть встроено, поскольку ваш поток не изменяет значение. Это означает, что он может не увидеть, что вы изменили его в другом потоке. - person Peter Lawrey; 31.10.2013
comment
@PeterLawrey, как компилятор может встроить это значение? скажем, в примере не задействована многопоточность. У меня есть этот цикл, печатающий поле, и другой метод, который записывает в это поле. Как компилятор может встроить этот доступ? это сгенерирует неправильный код! - person lysergic-acid; 01.11.2013
comment
@lysergic-acid Вы совершенно правы, поэтому вы должны объявить эту переменную как volatile. - person Dawood ibn Kareem; 01.11.2013
comment
@lysergic-acid Но у вас нет другого метода для этого потока, и вы не вызываете конструкцию, безопасную для потоков. Простое добавление пустого блока synchronized() { } будет означать, что JIT этого не сделает. ="nofollow noreferrer">vanillajava.blogspot.co.uk/2012/01/ - person Peter Lawrey; 01.11.2013
comment
Хорошая статья, @PeterLawrey. Спасибо, что поделился. - person Dawood ibn Kareem; 01.11.2013

как вы собираетесь обновлять переменную int? если это прямое изменение, поэтому полезно установить переменную как volatile.
public volatile int myValue = 0;
но обновление read-modify, поэтому вам нужно синхронизировать потоки друг с другом.
и [эта ссылка] будет полезна для понимания volatile

person Community    schedule 31.10.2013

примитивные типы, такие как int в Java, читаются/записываются атомарно.

Поток всегда видит значение переменной, записанное каким-то потоком; не какое-то случайное значение, взятое из воздуха. Если 64-битные числовые переменные (длинные и двойные) не объявлены как volatile, они не обеспечивают безопасности из воздуха, поскольку JVM разрешено обрабатывать 64-битное значение как две 32-битные операции выборки. Поэтому, даже если вы используете примитивный тип, вы должны использовать Volatile.

Еще одна вещь в многопоточности есть две вещи: 1. атомарность 2. видимость

Volatile гарантирует часть видимости, а не часть атомарности. поэтому, если вы увеличиваете переменную, такую ​​как K++, чем вы должны synchronize, иначе вы не гарантируете часть atomicity.

person Trying    schedule 31.10.2013
comment
Однако делает ли volatile 64-битную запись атомарной (в 32-битной системе)? Я не очень хорошо знаком, но могу предположить, что если один поток запишет 0xDEADDEADBEEFBEEF в ранее нулевое 64-битное значение, то поток чтения может увидеть только 0x00000000BEEFBEEF. - person Jonathon Reinhart; 31.10.2013