|
|
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
class A { int x; A() { x = 42; } } Thread 1: A a = new A(); Thread 2: System.out.println(a.x); Правильно и я понимаю, что: Ни voaltile int x , ни volatile A a не гарантируют того, что Thread 2 не напишет "0"? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 00:29 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
а как ты ссылку передаешь в другой поток? Будет работать даже без volatile всегда, если только ты ссылку this не передаешь в конструкторе сразу после x=42. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 00:42 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
В вашем коде присутствует как data race, который может приводить к упомянутым вами эффектам, так и race condition, который может приводить к NPE. Вариант 1: Без volatile - Может вылететь NPE, так как из-за race condition Thread 2 прочитает a = null. - Может отпечатать 0 из-за data race. - Может отпечатать 42. Вариант 2: volatile int x - Может вылететь NPE, так как из-за race condition Thread 2 прочитает a = null. - Может отпечатать 0 из-за data race; volatile здесь не спасет, так как он допускает, что бы последующие записи произошли до волатильной записи. - Может отпечатать 42. Вариант 3: volatile A a - Может вылететь NPE, так как из-за race condition Thread 2 прочитает a = null. - Может отпечатать 42. А вот 0 отпечатан не будет никогда, так как все записи, идущие до волатильной записи в "a" должны быть завершены до нее. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 01:16 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
Да, во втором варианте от распечатки 0 вас спас бы final int x . ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 01:18 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
забыл ник, да мало ли как передаю. class Container { A a; public static void main(String[] args) { //thread1: Thread writerThread = new Thread() { public void run() { a = new A(); } }; writerThread.start(); //thread2: Thread readerThread = new Thread() { public void run() { System.out.println(a.x); } }; readerThread.start(); } ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 01:30 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
DEVcoachДа, во втором варианте от распечатки 0 вас спас бы final int x . Если ссылка A a передается в другой поток после A a = new A(), то x=42 гарантированно выполнится, потому что старт потока гарантирует hb, так что ваши рассуждения применимы только к такому коду Код: java 1. 2. 3. 4. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 01:32 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
Dymytryзабыл ник, да мало ли как передаю. class Container { A a; public static void main(String[] args) { //thread1: Thread writerThread = new Thread() { public void run() { a = new A(); } }; writerThread.start(); //thread2: Thread readerThread = new Thread() { public void run() { System.out.println(a.x); } }; readerThread.start(); } да, что-то я прогнал. DEVCoach прав ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 01:35 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
DEVcoachВ вашем коде присутствует как data race, который может приводить к упомянутым вами эффектам, так и race condition, который может приводить к NPE. Вариант 1: Без volatile - Может вылететь NPE, так как из-за race condition Thread 2 прочитает a = null. - Может отпечатать 0 из-за data race. - Может отпечатать 42. Вариант 2: volatile int x - Может вылететь NPE, так как из-за race condition Thread 2 прочитает a = null. - Может отпечатать 0 из-за data race; volatile здесь не спасет, так как он допускает, что бы последующие записи произошли до волатильной записи. - Может отпечатать 42. Вариант 3: volatile A a - Может вылететь NPE, так как из-за race condition Thread 2 прочитает a = null. - Может отпечатать 42. А вот 0 отпечатан не будет никогда, так как все записи, идущие до волатильной записи в "a" должны быть завершены до нее. Да здесь во всех трех вариантах NPE, 0, 42. А что за "волатильная запись"? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 14:52 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
no56892 , Волатильная запись = запись в volatile поле. Опишите последовательность действий, которые могут привести к 0 в третьем варианте. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 15:48 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
DEVcoach, Кхм, Присваивание значений аттрибутам происходит в конструкторе, т.е. : Код: java 1. 2. 3. 4. 5. А чем volatile отличается от не-волатайл на Ваш взгляд? Если можно то конкретными командами процессора (можно условными), а не общими терминами. Я сейчас сам копаюсь с JIT, дак вот ИМХО понять как работает JMM можно только узнав во что это превращается после JIT компиляции. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 17:28 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
no56892 , Понять, как работает JMM с помощью assembly, можно только в том случае, если вы досконально знаете, как генерирует код JIT. Поэтому, если вы не являетесь разработчиком c2-компилятора, ваши шансы понять JMM через чтение ассемблерного кода стремятся к нулю. Если же вы все таки являетесь им, то вы итак знаете JMM А если серьезно, то JMM как раз и был создан для того, что бы его понимали рядовые прикладные программисты, далекие от железа, ассемблера, и прочих низкоуровневых вещей. И понять его без ассемблера можно. Как раз на основе абстрактных примеров, которые вы почему-то просите не приводить. Теперь по поводу вашего комментария. Мы говорим, что у нас есть следующий класс: Код: java 1. 2. 3. И мы в одном потоке создаем инстанс этого класса, и записываем его в переменную "a", расшаренную между потоками. Вот она: Код: java 1. 2. 3. А другой поток делает следующее: Код: java 1. Вопрос: что распечатает второй поток? Вариант 1: Вылетит NPE. Вот один из сценариев, который может к этому привести: 1) Thread1: ref = malloc(A.class); // JVM аллоцировала объект, и держит его адрес где-то у себя в потрохах; 2) Thread1: STORE ref.x = 42; // Присвоили дефолтное значение переменной x; 3) Thread2: LOAD a; // Разыменовали "a", что бы пробраться к "x"; 4) Thread2: LOAD a.x; // Словили NPE, так как "a" все еще null. 5) Thread1: STORE a = ref; // Записали адрес объекта в переменную "a"; Вариант 2: Будет отпечатано 42. Это произойдет в следующем случае (цифра в начале строки - условный "такт процессора"): 1) Thread1: ref = malloc(A.class); // JVM аллоцировала объект, и держит его адрес где-то у себя в потрохах; 2) Thread1: STORE ref.x = 42; // Присвоили дефолтное значение переменной x; 3) Thread1: STORE a = ref; // Записали адрес объекта в переменную "a"; 4) Thread2: LOAD a; // Разыменовали "a", что бы пробраться к "x"; 5) Thread2: LOAD a.x; // Добрались до x. Вариант 3: Будет отпечатано 0. Вот один из сценариев, который может к этом привести: 1) Thread1: ref = malloc(A.class); // JVM аллоцировала объект, и держит его адрес где-то у себя в потрохах; 2) Thread1: STORE a = ref; // Записали адрес объекта в переменную "a"; 3) Thread2: LOAD a; // Разыменовали "a", что бы пробраться к "x"; 4) Thread2: LOAD a.x; // Добрались до x, а он все еще 0! 5) Thread1: STORE ref.x = 42; // Присвоили дефолтное значение переменной x; То есть у нас запись в ref.x "убежала" за запись в "a". Для потока Thread1 это не страшно, а вот для потока Thread2 это все ломает. Теперь мы пишем вот это: Код: java 1. 2. 3. Что это меняет? А то, что теперь записи, которые шли до записи в "а", нельзя переставлять за "а". Поэтому такой сценарий у нас допустим: 1) Thread1: ref = malloc(A.class); // JVM аллоцировала объект, и держит его адрес где-то у себя в потрохах; 2) Thread1: STORE ref.x = 42; // Присвоили дефолтное значение переменной x; 3) Thread1: VOLATILE STORE a = ref; // Записали адрес объекта в переменную "a"; А такой нет: 1) Thread1: ref = malloc(A.class); // JVM аллоцировала объект, и держит его адрес где-то у себя в потрохах; 2) Thread1: VOLATILE STORE a = ref; // Записали адрес объекта в переменную "a"; 3) Thread1: STORE ref.x = 42; // ЗАПРЕЩЕНО! Поэтому, в таком случае третий сценарий с отпечаткой нуля никогда не возникнет. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 17:55 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
no56892 , И еще пару моментов: - volatile распространяется только на ту переменную, которую вы объявили volatile, но не на ее "внутренности". - нет такого понятия "чтение из памяти" или "чтение не из памяти". Любая переменная может быть прочитана из памяти. Любая переменная может быть прочитана из кэша процессора или записана в него (это на случай, если вы где-то услышали миф, что "volatile переменные не кэшируются". Единственное отличие волатильных переменных от обычных - к ним (и в кком-то смысле "вокруг" них) неприменимы определенные оптимизации, которые применимы к обычным переменным. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 18:00 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
DEVcoach no56892 , И еще пару моментов: - volatile распространяется только на ту переменную, которую вы объявили volatile, но не на ее "внутренности". - нет такого понятия "чтение из памяти" или "чтение не из памяти". Любая переменная может быть прочитана из памяти. Любая переменная может быть прочитана из кэша процессора или записана в него (это на случай, если вы где-то услышали миф, что "volatile переменные не кэшируются". Единственное отличие волатильных переменных от обычных - к ним (и в кком-то смысле "вокруг" них) неприменимы определенные оптимизации, которые применимы к обычным переменным. -Согласен -Ну кэш тут вообще ни при чем. Кэш процессора - это когда в инструкции содержится адрес памяти и внутренний контроллер CPU определяет есть ли она в кэшэ или нет, если нет - забрать из памяти и положить в кэш. Здесь более точно: регистры vs память. Про оптимизации о которых вы говорите, это как раз и есть взять значение из регистра (а не из памяти/кэша - без разницы). Пример: i = i +1; i = i -1; Non-volatile: Код: java 1. 2. 3. 4. Volatile: Код: java 1. 2. Т.е. никаких других эфектов от volatile я не могу представить. Поэтому, не вижу причин, почему бы если сделать volatile там невозможно появление 0? Код: java 1. 2. 3. 4. Пруф? Переменные только в исходном коде. В рантайме - адреса и смещения. И такой инструкции как volatile store не существует. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 18:37 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
DEVcoach, Еще кстати дефолтное значение 42 на самом деле не совсем дефолтное. Т.к. память под новые объекты выделяется из ранее использовавшейся, то там "белый шум", и для начала при создании объекта JVM заполняет ее нулями, а уже потом в конструкторе присваивает (дефолтное с точки зрения программиста) значение 42. То есть если 2й поток каким-то чудом обратится по адресу объекта+смещение аттрибута ДО конструктора, то там будет соответственно 0.ИМХО И volatile здесь ни при чем. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 18:57 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
no56892 , Ваши рассуждения ошибочны, так как вы забываете про самое главное - acquire/release семантику. Я не просто так написал "запрещает оптимизации применительно к самой переменной, и "вокруг" нее". Вы написали про саму переменную - да, с ней нельзя заигрывать в регистрах, если сказано "прочитать", значит надо прочитать и точка. А acquire/release - это как раз про то, что происходит "вокруг" volatile переменной. А именно: 1) При записи в volatile переменную срабатывает release-семантика. Это означает следующее: к моменту завершения волатильной записи должны быть завершены все записи, которые по коду шли до нее. Поэтому вот такой сценарий оказывается запрещенным: Код: java 1. 2. 3. Ведь по коду у нас сначала идет запись в "x", а потом запись в "a". А в приведенном примере сначала в "а", а потом в "х". volatile запрещает это делать. 2) При чтении из volatile переменной срабатывает acquire-семантика, которая означает следующее: "если при чтении из переменной вы увидели некоторое значение, которое было записано с release-семантикой, значит вы гарантированно увидите и все то, что было записано до этого". Достигается это путем выставления вокруг volatile-чтений/записей специальных барьеров памяти. Подробнее вот здесь: http://gee.cs.oswego.edu/dl/jmm/cookbook.html (сейчас почему-то не открывается). В нашем случае мы читаем "a", и видим в ней не-null. Это означает, что к моменту чтения запись в "a" из другого потока уже завершена, а значит завершена и запись в "x", так как по коду запись в "х" предшествует записи в "а". P.S.: Что значит "инструкции volatile store не существует", я не понял. Мы же говорим про логическое поведение, а не про конкретные инструкции байткода или процессора. На уровне байткода volatile store действительно нет, но такая переменная в байткоде отмечается специальным флагом, которая говорит JVM, что это не простая запись, а волатильная. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 18:58 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
no56892DEVcoach, Еще кстати дефолтное значение 42 на самом деле не совсем дефолтное. Т.к. память под новые объекты выделяется из ранее использовавшейся, то там "белый шум", и для начала при создании объекта JVM заполняет ее нулями, а уже потом в конструкторе присваивает (дефолтное с точки зрения программиста) значение 42. То есть если 2й поток каким-то чудом обратится по адресу объекта+смещение аттрибута ДО конструктора, то там будет соответственно 0.ИМХО И volatile здесь ни при чем.Так в том то и дело, что если вы объявите "volatile A a", то вы не сможете простучаться к "a.x" до того, как "a.x" будет проинициализированно. Вы либо словите NPE, либо прочитаете 42, третьего не дано. Гуглить по фразе "java volatile piggybacking". В этом и заключается вся суть volatile. Если бы это было не так, то толку от этого ключевого слова было бы ноль. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 19:01 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
no56892Пример: i = i +1; i = i -1; Non-volatile: Код: java 1. 2. 3. 4. Volatile: Код: java 1. 2. Т.е. никаких других эфектов от volatile я не могу представить. Поэтому, не вижу причин, почему бы если сделать volatile там невозможно появление 0?Ну это, разумеется, неправильно. Процессор может что-то делать со значениями только в регистрах. Отличие на уровне ассемблера состоит лишь в том, что компилятору запрещено делать оптимизации с волатилями. Пример. Есть код: Код: java 1. 2. 3. 4. 5. 6. 7. 8. Как он будет выглядеть в ассемблере? Да хз, компилятор это черный ящик, вариантов десятки, сотни. Но вот один пример - умный register allocator компилятора может сделать следующее: Код: java 1. 2. 3. 4. 5. 6. 7. 8. 9. Видите в чем прикол? Мы у нас в коде было два инкремента каждой переменной, то есть по два чтения и две записи каждой. А в память в итоге было только по одному чтению и записи, а на промежуточных результатах компилятор сэкономил! И правильно сделал - ему никто этого не запрещал! А еще он в конце переставил записи y и х. Почему? Да хз, может быть это было выравнивание инструкций в процессе комплияции, А может еще что-то, хз. А теперь делаем y volatile. Что меняется? Код: java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. В итоге, как видите, мы уже не может особо ничего оптимизировать - это запрещено волатилем. И если в первом случае мы 4 чтения и 4 записи смогли превратить в 2 чтения и 2 записи, то здесь такое уже не прокатывает. Здесь у нас честные 2 чтения волатиля, честные две записи волатиля. Плюс два честные записи неволатильного x, который шел до записи в волатиль. А сэкономить мы смогли только на одном чтении неволатильной переменной. Итого 3 чтения и 4 записи. Плюс еще два барьера памяти впендюрили. И зареордерить запись x с записью y не смогли. Вот оно как на самом деле внутри происходит. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 19:22 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
DEVcoachno56892Пример: i = i +1; i = i -1; Non-volatile: Код: java 1. 2. 3. 4. Volatile: Код: java 1. 2. Т.е. никаких других эфектов от volatile я не могу представить. Поэтому, не вижу причин, почему бы если сделать volatile там невозможно появление 0?Ну это, разумеется, неправильно. Процессор может что-то делать со значениями только в регистрах. Отличие на уровне ассемблера состоит лишь в том, что компилятору запрещено делать оптимизации с волатилями. Пример. Есть код: Код: java 1. 2. 3. 4. 5. 6. 7. 8. Как он будет выглядеть в ассемблере? Да хз, компилятор это черный ящик, вариантов десятки, сотни. Но вот один пример - умный register allocator компилятора может сделать следующее: Код: java 1. 2. 3. 4. 5. 6. 7. 8. 9. Видите в чем прикол? Мы у нас в коде было два инкремента каждой переменной, то есть по два чтения и две записи каждой. А в память в итоге было только по одному чтению и записи, а на промежуточных результатах компилятор сэкономил! И правильно сделал - ему никто этого не запрещал! А еще он в конце переставил записи y и х. Почему? Да хз, может быть это было выравнивание инструкций в процессе комплияции, А может еще что-то, хз. А теперь делаем y volatile. Что меняется? Код: java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. В итоге, как видите, мы уже не может особо ничего оптимизировать - это запрещено волатилем. И если в первом случае мы 4 чтения и 4 записи смогли превратить в 2 чтения и 2 записи, то здесь такое уже не прокатывает. Здесь у нас честные 2 чтения волатиля, честные две записи волатиля. Плюс два честные записи неволатильного x, который шел до записи в волатиль. А сэкономить мы смогли только на одном чтении неволатильной переменной. Итого 3 чтения и 4 записи. Плюс еще два барьера памяти впендюрили. И зареордерить запись x с записью y не смогли. Вот оно как на самом деле внутри происходит. Во-первых, ADD Description Adds the destination operand (first operand) and the source operand (second operand) and then stores the result in the destination operand. The destination operand can be a register or a memory location ; the source operand can be an immediate, a register, or a memory location . (However, two memory operands cannot be used in one instruction.) When an immediate value is used as an operand, it is sign-extended to the length of the destination operand format. Во -вторых, Ну вы правильно написали все, только вот почему компилятор не может переставлять volatile-запись с не-volatile? пруф? А нафига там вообе нужны барьеры памяти? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 19:40 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
no56892DEVcoachпропущено... Ну это, разумеется, неправильно. Процессор может что-то делать со значениями только в регистрах. Отличие на уровне ассемблера состоит лишь в том, что компилятору запрещено делать оптимизации с волатилями. Пример. Есть код: Код: java 1. 2. 3. 4. 5. 6. 7. 8. Как он будет выглядеть в ассемблере? Да хз, компилятор это черный ящик, вариантов десятки, сотни. Но вот один пример - умный register allocator компилятора может сделать следующее: Код: java 1. 2. 3. 4. 5. 6. 7. 8. 9. Видите в чем прикол? Мы у нас в коде было два инкремента каждой переменной, то есть по два чтения и две записи каждой. А в память в итоге было только по одному чтению и записи, а на промежуточных результатах компилятор сэкономил! И правильно сделал - ему никто этого не запрещал! А еще он в конце переставил записи y и х. Почему? Да хз, может быть это было выравнивание инструкций в процессе комплияции, А может еще что-то, хз. А теперь делаем y volatile. Что меняется? Код: java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. В итоге, как видите, мы уже не может особо ничего оптимизировать - это запрещено волатилем. И если в первом случае мы 4 чтения и 4 записи смогли превратить в 2 чтения и 2 записи, то здесь такое уже не прокатывает. Здесь у нас честные 2 чтения волатиля, честные две записи волатиля. Плюс два честные записи неволатильного x, который шел до записи в волатиль. А сэкономить мы смогли только на одном чтении неволатильной переменной. Итого 3 чтения и 4 записи. Плюс еще два барьера памяти впендюрили. И зареордерить запись x с записью y не смогли. Вот оно как на самом деле внутри происходит. Во-первых, ADD Description Adds the destination operand (first operand) and the source operand (second operand) and then stores the result in the destination operand. The destination operand can be a register or a memory location ; the source operand can be an immediate, a register, or a memory location . (However, two memory operands cannot be used in one instruction.) When an immediate value is used as an operand, it is sign-extended to the length of the destination operand format. Во -вторых, Ну вы правильно написали все, только вот почему компилятор не может переставлять volatile-запись с не-volatile? пруф? А нафига там вообе нужны барьеры памяти? Хотя может Вы и правы, я думал volatile то же, что и в С. Надо смтореть. Но тем не менее, мне не верится, что с volatile мы не увидим 0. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 19:45 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
no56892Хотя может Вы и правы, я думал volatile то же, что и в С. Надо смотреть. Но тем не менее, мне не верится, что с volatile мы не увидим 0.Вот оно что :-) volatile в Java от совсем не то же, что и в C/C++, и используется совершенно иначе. Оригинальный пруф того о чем я говорю, находится здесь http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.2 Главы 17.4.2 - 17.4.5. Но чтобы действительно понять, что там написано, вам придется изрядно попыхтеть, ибо каждое предложение там эквивалентно нескольким обзацам на нормальном человеческом языке. Так что предлагаю вам поверить мне наслово :-) Про регистры согласен. В случае примитивных операций может быть так, как вы и сказали. Чтобы отбросить такие простейшие варианты, давайте заменим инкремент, на любую другую функцию, принимающую один операнд. Например, возведение в степень. сдвиги ... да что угодно, что нельзя описать одной соответствующей инструкцией процессора. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 31.05.2014, 20:05 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
А кстати, а каким образом здесь в каком-либо случае вообще возможен 0? Если JLS 12.5 Just before a reference to the newly created object is returned as the result , the indicated constructor is processed to initialize the new object using the following procedure: ... 4. Execute the instance initializers and instance variable initializers for this class , assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. ??? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 01.06.2014, 01:50 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
no56892, А все понял, пора спать ) ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 01.06.2014, 02:01 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
no56892, Хотя нет все-таки, вопрос остается открытым... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 01.06.2014, 02:08 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
no56892no56892, Хотя нет все-таки, вопрос остается открытым... Есть такое понятие - program order . Это такой порядок выполнения инструкций, который указан у вас в коде. И действительно, согласно program order инициализация полей объекта происходит до выхода из конструктора. А есть настоящий порядок выполнения - тот, который реально происходит на железе. И он там совсем другой из-за: - out-of-order execution процессора; - оптимизаций компилятора. На это переупорядочивание накладываются определенные ограничения: в рамках одного потока инструкции можно переставлять только так, что бы это было неотличимо от program order. Поэтому в однопоточной программе вы никогда не столкнетесь с a.x = 0. А на многопоточные программы эти гарантии не распространяются. Поэтому вы можете в одном потоке "видеть", что вы вышли из конструктора, и a.x = 42, а в другом потоке "видеть", что a.x = 0. И в этом случае у нас есть только такая гарантия: "при чтении переменной поток гарантированно увидит в ней одно из значений, записанных в нее ранее". То есть, если я записывал в переменную из разных потоков 0, 1, 2, 3, то на очередном ее чтении я не обязательно увижу самое свежее значение 3, но могу увидеть любое предыдущее значение. Такие дела :-) Вообще, если вы незнакомы с моделью памяти Java, то лучше сначала поизучать ее самостоятельно, и потом уже задавать вопросы. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 01.06.2014, 09:17 |
|
||
|
Безопасная публикация volatile.
|
|||
|---|---|---|---|
|
#18+
Перечитал тему и задумался. DEVcoach...все записи, идущие до волатильной записи в "a" должны быть завершены до нее. 1. Значит ли это, что безопасно публикуя объект через volatile мы также безопасно публикуем весь граф атрибутов внутри этого объекта? 2. Можно ли сказать то же самое про публикацию через final? ps DEVcoachВариант 2: volatile int x - Может отпечатать 0 из-за data race; volatile здесь не спасет, так как он допускает, что бы последующие записи произошли до волатильной записи. Не совсем понятен смысл этой фразы. А нельзя сказать проще: здесь дело в том, что считывание переменной x может произойти после записи туда дефолтного значения и до записи значения в конструкторе? И кроме того, а может оно произойти до записи дефолтного значения 0 и что это тогда будет? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 15.08.2014, 02:59 |
|
||
|
|

start [/forum/topic.php?fid=59&startmsg=38657559&tid=2126736]: |
0ms |
get settings: |
5ms |
get forum list: |
10ms |
check forum access: |
2ms |
check topic access: |
2ms |
track hit: |
166ms |
get topic data: |
8ms |
get forum data: |
2ms |
get page messages: |
34ms |
get tp. blocked users: |
1ms |
| others: | 203ms |
| total: | 433ms |

| 0 / 0 |
