Re[13]: volatile
От: remark Россия http://www.1024cores.net/
Дата: 06.10.09 10:11
Оценка: +1
Здравствуйте, Golovach Ivan, Вы писали:

GI>Т.е. я утверждаю, что

GI>1) контролируемый data race не плох. Это просто инструмент. Да утонченный, да опасный, но работающий по спеке и значит контролируемый.
GI>2) Incorrectly Synchronized Program не является Incorrectly Program. Т.е. стоит понимать как <Incorrectly Synchronized> Program, а не как Incorrectly <Synchronized Program>. Incorrectly Synchronized не является недопустимым, это просто свойство.


С этим я согласен.
Спецификация языка вообще не оперирует понятиями "плохо"/"хорошо", она только вводит семантику для конструкций языка (хотя фразы типа "Incorrectly Synchronized Programs Exhibit Surprising Behaviors" видеть в спецификации несколько... необычно, появляется впечатление, что читаешь не формальную спецификацию языка, а литературное произведение, ну это так, к слову).
Т.е. если есть некий код, и согласно спецификации он делает то, что от него требуется, то собственно это и имеем, не больше и не меньше, код с таким-то поведением.
Вообще в своё время при обсуждении новой модели памяти шли дискусси о "correct but incorrectly synchronized code".

Я был не согласен с несколько другим моментом. Во-первых, с тем какие переупорядочивания возможны, во-вторых, с формой подачи этого.
Если уж говорить об этом, то ИМХО первое, что нужно говорить, так это что есть корректно- и некорректно-синхронизированный код. В целом некорректно-синхронизированный код нужно рассматривать как просто ошибочный код. И программист должен ориентироваться на корректно-синхронизированный код, для него гарантируется полная последовательная консистентность, т.е. отсутствие каких-либо переупорядочиваний.
После этого можно добавить (а можно и не добавлять), что в принципе для некорректно-синхронизированного кода предоставляются вполне определенные гарантии, которые можно использовать в некоторых случаях. НО это очень-очень специфические случаи, и вы должны быть на 200% уверены, что вы делаете.
Всё же некорректно-синхронизированный код рассматривался создателями модели памяти именно как некорректный код. Вот например цитата из "The Java Memory Model" (http://rsim.cs.illinois.edu/Pubs/popl05.pdf) (обратите внимание на выделенное):

The bulk of the effort for the revision, and our focus here,
was on understanding the requirements for incorrectly synchronized
code. The previous strategy of leaving the semantics
for incorrect programs unspecified is inconsistent with
Java’s security and safety guarantees. Such a strategy has
been used in some prior languages. For example, Ada simply
defines unsynchronized code as “erroneous” [1]. The reasoning
behind this is that since such code is incorrect (on some
level), no guarantees should be made when it occurs. This is
similar to the strategy that some languages take with array
bounds overflow – unpredictable results may occur, and it
is the programmer’s responsibility to avoid these scenarios.
The above approach does not lend itself to the writing
of secure and safe code. In an ideal world, every programmer
would write correct code all of the time. In our world,
programs frequently contain errors; not only does this cause
code to misbehave, but it can also allows attackers to violate
safety assumptions in a program (as is true with buffer
overflows). Our earlier work has described the dangers of
such scenarios in more detail [28].
Program semantics must be completely defined: if programmers
don’t know what their code is doing, they won’t
be able to know what their code is doing wrong. The second
broad requirement of the Java model is to provide a clear
and definitive semantics for how code should behave when it
is not correctly written
, but without substantially affecting
current compilers and hardware.


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



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[14]: volatile
От: remark Россия http://www.1024cores.net/
Дата: 06.10.09 10:41
Оценка:
Здравствуйте, remark, Вы писали:

R>После этого можно добавить (а можно и не добавлять), что в принципе для некорректно-синхронизированного кода предоставляются вполне определенные гарантии, которые можно использовать в некоторых случаях. НО это очень-очень специфические случаи, и вы должны быть на 200% уверены, что вы делаете.


Единственный момент, который мне не до конца понятен касательно некорректно-синхронизированного кода, — это обеспечение когерентности.
С++ стандарт говорит "Implementations should make atomic stores visible to atomic loads within a reasonable amount of time". Т.е. этим гарантируется распространение изменений между потоками. Действия, совершённые в одном потоке, должны стать видны другим потокам в конечное время.
CLI/C# стандарт лажается в этом месте и не даёт таких гарантий. Но это я уже донёс до разработчиков, они собираются это пофиксить.
В стандарте Java я тоже вроде не вижу никаких подобных гарантий. Однако для volatile переменных можно быть уверенным, что по-крайней мере у разработчиков компиляторов есть такое намерение. А вот что касается обычных переменных это совсем не очевидно.

К чему это может привести? К тому, что изменения обычной переменной в одном потоке НИКОГДА не будут видны другим потокам (либо вследствие того, что первый поток, так и не запишет изменение в память; либо другой поток так и не будет перечитывать значение из памяти).
Если это действительно так для Java, то это ещё существенно сужает круг применимости "benign data race" ещё больше. В кода рассчёта хэша для строк это не является проблемой, т.к. всё равно остаётся гарантия, что каждый поток рассчитает хэш для строки не более одного раза. А вот если необходимо прямо или косвенно просигнализировать другой поток, то сигнал может не дойти до потока. В C# для борьбы с этим можно применять специальный аттрибут для функции, который запрещает встраивание функции, и это вроде как позволяет получить гарантию когерентности. В С++ можно просто законным способом отделить атомарность и видимость от упорядочивания. Есть ли что-то в Java для борьбы с этим — не знаю.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[14]: volatile
От: remark Россия http://www.1024cores.net/
Дата: 06.10.09 11:02
Оценка:
Здравствуйте, remark, Вы писали:

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

R>Если уж говорить об этом, то ИМХО первое, что нужно говорить, так это что есть корректно- и некорректно-синхронизированный код. В целом некорректно-синхронизированный код нужно рассматривать как просто ошибочный код. И программист должен ориентироваться на корректно-синхронизированный код, для него гарантируется полная последовательная консистентность, т.е. отсутствие каких-либо переупорядочиваний.
R>После этого можно добавить (а можно и не добавлять), что в принципе для некорректно-синхронизированного кода предоставляются вполне определенные гарантии, которые можно использовать в некоторых случаях. НО это очень-очень специфические случаи, и вы должны быть на 200% уверены, что вы делаете.

И переупорядочивания в этом случае не сводятся только к тому, что обычные обращения могут подниматься за volatile сохранение, и опускаться через volatile загрузку.
Вот, например, хороший пример помедитировать:

volatile int V1 = 0;
volatile int V2 = 0;
int X = 0;
int Y = 0;

thread1()
{
  while (V1 != 0) {}
  X = 1;
  V1 = 1;
  while (V1 != 1) {}
  Y = 1;
  V1 = 2;
}

thread2()
{
  while (V2 != 0) {}
  int R1 = Y;
  V2 = 1;
  while (V2 != 1) {}
  int R2 = X;
  V2 = 2;
  if (R1 == 1 && R2 == 0) {...}
}


Что тут переупорядочивается, когда срабатывает условие в потоке 2? Вроде как сохранения и загрузки в/из X/Y не могут никуда двигаться, т.к. сверху volatile load, а снизу — volatile store.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[15]: volatile
От: Nicht Россия  
Дата: 06.10.09 11:27
Оценка:
Здравствуйте, remark, Вы писали:

R>Что тут переупорядочивается, когда срабатывает условие в потоке 2? Вроде как сохранения и загрузки в/из X/Y не могут никуда двигаться, т.к. сверху volatile load, а снизу — volatile store.


R>


Блин. Честно прочитал все ветку. Не понял одного. Кто о чем и с кем спорит? Тоесть приводят куски кода и говорят: "Истину вам говорю, 4 мая 1925 года Земля налетит на небесную осью". Тоесть, никто из ораторов даже не сказал а как на самом деле работат приведенный код.
Вот и сейчас. Лично мне кажется что этот код корректный. И никакого реордеринга быть не должно, исходя из спеки. И все будет хорошо. И все поженятся. Или у тебя есть печальный опыт что подобное уловие срабатывало?
Re[16]: volatile
От: remark Россия http://www.1024cores.net/
Дата: 06.10.09 18:05
Оценка:
Здравствуйте, Nicht, Вы писали:

R>>Что тут переупорядочивается, когда срабатывает условие в потоке 2? Вроде как сохранения и загрузки в/из X/Y не могут никуда двигаться, т.к. сверху volatile load, а снизу — volatile store.


N>Блин. Честно прочитал все ветку. Не понял одного. Кто о чем и с кем спорит? Тоесть приводят куски кода и говорят: "Истину вам говорю, 4 мая 1925 года Земля налетит на небесную осью". Тоесть, никто из ораторов даже не сказал а как на самом деле работат приведенный код.

N>Вот и сейчас. Лично мне кажется что этот код корректный. И никакого реордеринга быть не должно, исходя из спеки. И все будет хорошо. И все поженятся. Или у тебя есть печальный опыт что подобное уловие срабатывало?

Если дело касается портабельного кода, то я предпочитаю не полагаться на опыт и на то, что что-то где-то работает или не работает. То, что действительно имеет значение, это — спецификация языка.
Согласно спецификации сохранение в Х НЕ happens-before загрузки из Х, даже если загрузка из Y вернула 1. Следовательно загрузка из Х может вернуть или 1 или начальное значение 0.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[16]: volatile
От: Golovach Ivan Украина http://kharkovitcourses.blogspot.com
Дата: 06.10.09 19:30
Оценка:
Здравствуйте, Nicht, Вы писали:

N>Блин. Честно прочитал все ветку. Не понял одного. Кто о чем и с кем спорит?

Это не спор, это дискуссия.
Мы пытаемся найти истину. Как-оно-все-работает-на-самом-деле.
Re[15]: volatile
От: Golovach Ivan Украина http://kharkovitcourses.blogspot.com
Дата: 09.10.09 19:46
Оценка: 40 (1)
Здравствуйте, remark, Вы писали:

R>Здравствуйте, remark, Вы писали:


R>>После этого можно добавить (а можно и не добавлять), что в принципе для некорректно-синхронизированного кода предоставляются вполне определенные гарантии, которые можно использовать в некоторых случаях. НО это очень-очень специфические случаи, и вы должны быть на 200% уверены, что вы делаете.


R>Единственный момент, который мне не до конца понятен касательно некорректно-синхронизированного кода, — это обеспечение когерентности.

R>С++ стандарт говорит "Implementations should make atomic stores visible to atomic loads within a reasonable amount of time". Т.е. этим гарантируется распространение изменений между потоками. Действия, совершённые в одном потоке, должны стать видны другим потокам в конечное время.
R>CLI/C# стандарт лажается в этом месте и не даёт таких гарантий. Но это я уже донёс до разработчиков, они собираются это пофиксить.
R>В стандарте Java я тоже вроде не вижу никаких подобных гарантий. Однако для volatile переменных можно быть уверенным, что по-крайней мере у разработчиков компиляторов есть такое намерение.
Для обычных ничего не нашел. Детальный анализ гарантий для volatile немного загнал меня в тупик:
JVM Spec. Edition 2. CHAPTER 8. Threads and Locks.[http://java.sun.com/docs/books/jvms/second_edition/html/Threads.doc.html]:
"Variables are kept in a main memory that is shared by all threads."
"Every thread has a working memory in which it keeps its own working copy of variables that it must use or assign. As the thread executes a program, it operates on these working copies. The main memory contains the master copy of every variable. There are rules about when a thread is permitted or required to transfer the contents of its working copy of a variable into the master copy or vice versa."
"A use or assign operation is a tightly coupled interaction between a thread's execution engine and the thread's working memory."
"When data is copied from the main memory to a working memory, two actions must occur: a read operation performed by the main memory, followed some time later by a corresponding load operation performed by the working memory. When data is copied from a working memory to the main memory, two actions must occur: a store operation performed by the working memory, followed some time later by a corresponding write operation performed by the main memory. There may be some transit time between main memory and a working memory, and the transit time may be different for each transaction;"
8.7 Rules for volatile Variables:
"# A use operation by T on V is permitted only if the previous operation by T on V was load, and a load operation by T on V is permitted only if the next operation by T on V is use. The use operation is said to be "associated" with the read operation that corresponds to the load.
# A store operation by T on V is permitted only if the previous operation by T on V was assign, and an assign operation by T on V is permitted only if the next operation by T on V is store. The assign operation is said to be "associated" with the write operation that corresponds to the store."
Т.е. выходит, присвоение для volatile переменной соответствует выполняемым _в_этот_момент_ инструкциям assign+store, но вот инструкция write (to main memory) происходит !!!some time later!!!. Т.е. выходит, что в коде присвоение volatile переменной уже произошло и поток пошел дальше, но в main memory данное не установлено.
Я полагал, что assign, store и write выполняются вместе. Спасибо, Вы сподвигли меня на исследования JVM Spec.


R>А вот что касается обычных переменных это совсем не очевидно. К чему это может привести? К тому, что изменения обычной переменной в одном потоке НИКОГДА не будут видны другим потокам (либо вследствие того, что первый поток, так и не запишет изменение в память; либо другой поток так и не будет перечитывать значение из памяти).

Согласен. Классический пример приведен в книге Effective Java. Second Edition. Joshua Bloch:
Item 66: Synchronize access to shared mutable data:
"
// Broken! - How long would you expect this program to run?
public class StopThread {
  private static boolean stopRequested;
  public static void main(String[] args) throws InterruptedException {
    Thread backgroundThread = new Thread(new Runnable() {
      public void run() {
        int i = 0;
        while (!stopRequested)
          i++;
      }
    });
    backgroundThread.start();
    TimeUnit.SECONDS.sleep(1);
    stopRequested = true;
  }
}

You might expect this program to run for about a second, after which the main
thread sets stopRequested to true, causing the background thread’s loop to terminate.
On my machine, however, the program never terminates: the background
thread loops forever!"
Джошуа предлагает интерпретацию в виде единичного считывания флага вторым потоком
"This optimization is known as hoisting, and it is precisely what the HotSpot server
VM does."


R>Если это действительно так для Java, то это ещё существенно сужает круг применимости "benign data race" ещё больше. В кода рассчёта хэша для строк это не является проблемой, т.к. всё равно остаётся гарантия, что каждый поток рассчитает хэш для строки не более одного раза. А вот если необходимо прямо или косвенно просигнализировать другой поток, то сигнал может не дойти до потока. В C# для борьбы с этим можно применять специальный аттрибут для функции, который запрещает встраивание функции, и это вроде как позволяет получить гарантию когерентности. В С++ можно просто законным способом отделить атомарность и видимость от упорядочивания. Есть ли что-то в Java для борьбы с этим — не знаю.

Все что я знаю "промежуточное" между volatile и non-volatile переменными с точки зрения когерентности кэша это java.util.concurrent.atomic.Atomic*.lazySet(...) [http://java.sun.com/javase/6/docs/api/java/util/concurrent/atomic/package-summary.html]:
"lazySet has the memory effects of writing (assigning) a volatile variable except that it permits reorderings with subsequent (but not previous) memory actions that do not themselves impose reordering constraints with ordinary non-volatile writes. Among other usage contexts, lazySet may apply when nulling out, for the sake of garbage collection, a reference that is never accessed again."
[http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6275329]:
"As probably the last little JSR166 follow-up for Mustang,
we added a "lazySet" method to the Atomic classes
(AtomicInteger, AtomicReference, etc). This is a niche
method that is sometimes useful when fine-tuning code using
non-blocking data structures. The semantics are
that the write is guaranteed not to be re-ordered with any
previous write, but may be reordered with subsequent operations
(or equivalently, might not be visible to other threads) until
some other volatile write or synchronizing action occurs).

The main use case is for nulling out fields of nodes in
non-blocking data structures solely for the sake of avoiding
long-term garbage retention; it applies when it is harmless
if other threads see non-null values for a while, but you'd
like to ensure that structures are eventually GCable. In such
cases, you can get better performance by avoiding
the costs of the null volatile-write. There are a few
other use cases along these lines for non-reference-based
atomics as well, so the method is supported across all of the
AtomicX classes.

For people who like to think of these operations in terms of
machine-level barriers on common multiprocessors, lazySet
provides a preceeding store-store barrier (which is either
a no-op or very cheap on current platforms), but no
store-load barrier (which is usually the expensive part
of a volatile-write)."

P.S. Добавили в java 1.6. Возможно Вы не сталкивались.
Re[16]: volatile
От: remark Россия http://www.1024cores.net/
Дата: 10.10.09 12:46
Оценка:
Здравствуйте, Golovach Ivan, Вы писали:

R>>Единственный момент, который мне не до конца понятен касательно некорректно-синхронизированного кода, — это обеспечение когерентности.

R>>С++ стандарт говорит "Implementations should make atomic stores visible to atomic loads within a reasonable amount of time". Т.е. этим гарантируется распространение изменений между потоками. Действия, совершённые в одном потоке, должны стать видны другим потокам в конечное время.
R>>CLI/C# стандарт лажается в этом месте и не даёт таких гарантий. Но это я уже донёс до разработчиков, они собираются это пофиксить.
R>>В стандарте Java я тоже вроде не вижу никаких подобных гарантий. Однако для volatile переменных можно быть уверенным, что по-крайней мере у разработчиков компиляторов есть такое намерение.

GI>Для обычных ничего не нашел. Детальный анализ гарантий для volatile немного загнал меня в тупик:


Это нормально

GI>JVM Spec. Edition 2. CHAPTER 8. Threads and Locks.[http://java.sun.com/docs/books/jvms/second_edition/html/Threads.doc.html]:

GI>"Variables are kept in a main memory that is shared by all threads."
GI>"Every thread has a working memory in which it keeps its own working copy of variables that it must use or assign. As the thread executes a program, it operates on these working copies. The main memory contains the master copy of every variable. There are rules about when a thread is permitted or required to transfer the contents of its working copy of a variable into the master copy or vice versa."
GI>"A use or assign operation is a tightly coupled interaction between a thread's execution engine and the thread's working memory."
GI>"When data is copied from the main memory to a working memory, two actions must occur: a read operation performed by the main memory, followed some time later by a corresponding load operation performed by the working memory. When data is copied from a working memory to the main memory, two actions must occur: a store operation performed by the working memory, followed some time later by a corresponding write operation performed by the main memory. There may be some transit time between main memory and a working memory, and the transit time may be different for each transaction;"
GI>8.7 Rules for volatile Variables:
GI>"# A use operation by T on V is permitted only if the previous operation by T on V was load, and a load operation by T on V is permitted only if the next operation by T on V is use. The use operation is said to be "associated" with the read operation that corresponds to the load.
GI># A store operation by T on V is permitted only if the previous operation by T on V was assign, and an assign operation by T on V is permitted only if the next operation by T on V is store. The assign operation is said to be "associated" with the write operation that corresponds to the store."
GI>Т.е. выходит, присвоение для volatile переменной соответствует выполняемым _в_этот_момент_ инструкциям assign+store, но вот инструкция write (to main memory) происходит !!!some time later!!!. Т.е. выходит, что в коде присвоение volatile переменной уже произошло и поток пошел дальше, но в main memory данное не установлено.

Видимо write действительно может быть выполнен позже (собственно на практике так оно и происходит), НО для volatile в силе дополнительные гарантии, которые обусловлены happens-before отношениями. А покуда они выполняются, отложенная запись не может вызвать проблем.

GI>Я полагал, что assign, store и write выполняются вместе. Спасибо, Вы сподвигли меня на исследования JVM Spec.



R>>А вот что касается обычных переменных это совсем не очевидно. К чему это может привести? К тому, что изменения обычной переменной в одном потоке НИКОГДА не будут видны другим потокам (либо вследствие того, что первый поток, так и не запишет изменение в память; либо другой поток так и не будет перечитывать значение из памяти).


GI>Согласен. Классический пример приведен в книге Effective Java. Second Edition. Joshua Bloch:

GI>Item 66: Synchronize access to shared mutable data:
GI>"
// Broken! - How long would you expect this program to run?
GI>public class StopThread {
GI>  private static boolean stopRequested;
GI>  public static void main(String[] args) throws InterruptedException {
GI>    Thread backgroundThread = new Thread(new Runnable() {
GI>      public void run() {
GI>        int i = 0;
GI>        while (!stopRequested)
GI>          i++;
GI>      }
GI>    });
GI>    backgroundThread.start();
GI>    TimeUnit.SECONDS.sleep(1);
GI>    stopRequested = true;
GI>  }
GI>}

GI>You might expect this program to run for about a second, after which the main
GI>thread sets stopRequested to true, causing the background thread’s loop to terminate.
GI>On my machine, however, the program never terminates: the background
GI>thread loops forever!"
GI>Джошуа предлагает интерпретацию в виде единичного считывания флага вторым потоком
GI>"This optimization is known as hoisting, and it is precisely what the HotSpot server
GI>VM does."

Понятно.
Это существенно ограничивает область применимости обычных переменных для синхронизации. Большинство алгоритмов синхронизации (такие как мьютексы, очереди, стеки и т.д.) рассчитывают на гарантию когерентности, т.е. на распространение изменений в конечном счёте, и без неё работать не будет.


R>>Если это действительно так для Java, то это ещё существенно сужает круг применимости "benign data race" ещё больше. В кода рассчёта хэша для строк это не является проблемой, т.к. всё равно остаётся гарантия, что каждый поток рассчитает хэш для строки не более одного раза. А вот если необходимо прямо или косвенно просигнализировать другой поток, то сигнал может не дойти до потока. В C# для борьбы с этим можно применять специальный аттрибут для функции, который запрещает встраивание функции, и это вроде как позволяет получить гарантию когерентности. В С++ можно просто законным способом отделить атомарность и видимость от упорядочивания. Есть ли что-то в Java для борьбы с этим — не знаю.

GI>Все что я знаю "промежуточное" между volatile и non-volatile переменными с точки зрения когерентности кэша это java.util.concurrent.atomic.Atomic*.lazySet(...) [http://java.sun.com/javase/6/docs/api/java/util/concurrent/atomic/package-summary.html]:
GI>"lazySet has the memory effects of writing (assigning) a volatile variable except that it permits reorderings with subsequent (but not previous) memory actions that do not themselves impose reordering constraints with ordinary non-volatile writes. Among other usage contexts, lazySet may apply when nulling out, for the sake of garbage collection, a reference that is never accessed again."

GI>P.S. Добавили в java 1.6. Возможно Вы не сталкивались.


Занятно.
Не сталкивался раньше... ну хотя я вообще с Java сталкиваюсь только "постольку-поскольку".
Документация, как всегда для таких вопросов, оставляет желать лучшего. Не понятно, последующие операции могут подниматься вверх за lazySet() за счёт переупорядочиваний процессором или и компилятором. Из документации вроде как следует, что и компилятором, и тогда lazySet() получается практически таким же бесполезным как и обычные переменные, т.к. отсутствует гарантия когерентности. Хотя из здравого смысла разумно предположить, что намерение было допускать только переупорядочивания процессором, и тогда это — вполне полезная функция.
В смысле полезная до известного предела:

low-level atomics — the programming equivalent of playing Russian Roulette with all chambers loaded




1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[17]: volatile
От: remark Россия http://www.1024cores.net/
Дата: 10.10.09 13:24
Оценка: 1 (1)
Здравствуйте, remark, Вы писали:

GI>>Все что я знаю "промежуточное" между volatile и non-volatile переменными с точки зрения когерентности кэша это java.util.concurrent.atomic.Atomic*.lazySet(...) [http://java.sun.com/javase/6/docs/api/java/util/concurrent/atomic/package-summary.html]:


Кстати, может кому будет интересно.
Есть ещё sun.misc.Unsafe:
http://www.loongson.cn/svn/toolchain-gs/branches/auto-vectorization/gcc-4.3.0/libjava/classpath/vm/reference/sun/misc/Unsafe.java

В нём есть методы compareAndSwapXXX(), которые не дают никаких гарантий упорядочивания, и соотв. значительно быстрее обычного Atomic.compareAndSwap() на некоторых архитектурах. Они используются в нашумевшем hash map от Cliff Click.

Есть putOrderedХХХ(), которые видимо есть аналог lazySet().
Есть putХХХVolatile(), которые аналог volatile store (но его соотв. можно применять избирательно).
И есть putXXX(), которое видимо и есть то самое заветное "обычное сохранение, но с гарантией когерентности".
Кстати, если lazySet() реализован через Unsafe.putOrderedХХХ(), то он тоже должен предоставлять гарантию когерентности.
И соотв. есть аналоги для get, через которые можно получить "обычную загрузку, но с гарантией когерентности".

Хотя это всё есть "unsafe" и не портируемое, однако насколько я понимаю поддерживается не только компилятором Sun, собственно ссылка, которую я привёл, относится к gcj.

И так же на подходе Fences API:
http://gee.cs.oswego.edu/dl/jsr166/dist/docs/java/util/concurrent/atomic/Fences.html
Fences API будет позволять делать избирательную fine-grained синхронизацию в необходимых местах.
Хммм... хотя тут тоже для меня не понятно с когерентностью... вроде никаких явных гарантий нет... видимо я всё же упускаю что-то важное для Java



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[15]: volatile
От: Golovach Ivan Украина http://kharkovitcourses.blogspot.com
Дата: 10.10.09 13:43
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, remark, Вы писали:


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

R>>Если уж говорить об этом, то ИМХО первое, что нужно говорить, так это что есть корректно- и некорректно-синхронизированный код. В целом некорректно-синхронизированный код нужно рассматривать как просто ошибочный код. И программист должен ориентироваться на корректно-синхронизированный код, для него гарантируется полная последовательная консистентность, т.е. отсутствие каких-либо переупорядочиваний.
R>>После этого можно добавить (а можно и не добавлять), что в принципе для некорректно-синхронизированного кода предоставляются вполне определенные гарантии, которые можно использовать в некоторых случаях. НО это очень-очень специфические случаи, и вы должны быть на 200% уверены, что вы делаете.

R>И переупорядочивания в этом случае не сводятся только к тому, что обычные обращения могут подниматься за volatile сохранение, и опускаться через volatile загрузку.

R>Вот, например, хороший пример помедитировать:

R>
R>volatile int V1 = 0;
R>volatile int V2 = 0;
R>int X = 0;
R>int Y = 0;

R>thread1()
R>{
R>  while (V1 != 0) {}
R>  X = 1;
R>  V1 = 1;
R>  while (V1 != 1) {}
R>  Y = 1;
R>  V1 = 2;
R>}

R>thread2()
R>{
R>  while (V2 != 0) {}
R>  int R1 = Y;
R>  V2 = 1;
R>  while (V2 != 1) {}
R>  int R2 = X;
R>  V2 = 2;
R>  if (R1 == 1 && R2 == 0) {...}
R>}
R>


R>Что тут переупорядочивается, когда срабатывает условие в потоке 2? Вроде как сохранения и загрузки в/из X/Y не могут никуда двигаться, т.к. сверху volatile load, а снизу — volatile store.


По моему мнению, тут небольшое недоразумение. В отличии от моделей памяти процессоров, где (как я понимаю) инструкции LFENCE, SFENCE, MFENCE, locking instructions, etc. имеют ordering-свойства НЕ ЗАВИСЯЩИЕ ОТ АДРЕССА в памяти, в JMM happend-before (и связанные с ним ordering-гарантии) происходит ТОЛЬКО ПРИ ОБРАЩЕНИИ К ОДНОМУ ДАННОМУ:
JLS.ver3: Chapter 17. Threads and locks. [http://java.sun.com/docs/books/jls/third_edition/html/memory.html]
"17.4.4 Synchronization Order:
A write to a volatile variable (§8.3.1.4) v synchronizes-with all subsequent reads of v by any thread (where subsequent is defined according to the synchronization order)."
"17.4.5 Happens-before Order:
A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field."
И так как поток thread1 работает исключительно с V1, а поток thread2 работает исключительно с V2, то между ними не устанавливается отношение happend-before и, как следствие, во втором потоке может выполниться любое условие на декартовом произведении всех переменных со всеми значениями:
if (R1 == 0 && R2 == 0) {...}
if (R1 == 0 && R2 == 1) {...}
if (R1 == 1 && R2 == 0) {...}
if (R1 == 1 && R2 == 1) {...}

Для наглядности, достаточно предположить, что первый поток уже отработал и в его локальной памяти лежат X=1, Y=1, у второго потока изначально в локальной памяти лежало X=0, Y=0. Раз нет happend-before со вторым потоком, то локальная память второго потока не обязана "получить" значения X=1, Y=1, но может. И, как следствие, второй поток может увидеть любую комбинацию из того что есть у него и того что есть у первого потока.
Re[16]: volatile
От: remark Россия http://www.1024cores.net/
Дата: 10.10.09 14:42
Оценка:
Здравствуйте, Golovach Ivan, Вы писали:

GI>По моему мнению, тут небольшое недоразумение. В отличии от моделей памяти процессоров, где (как я понимаю) инструкции LFENCE, SFENCE, MFENCE, locking instructions, etc. имеют ordering-свойства НЕ ЗАВИСЯЩИЕ ОТ АДРЕССА в памяти, в JMM happend-before (и связанные с ним ordering-гарантии) происходит ТОЛЬКО ПРИ ОБРАЩЕНИИ К ОДНОМУ ДАННОМУ:


Я понимаю. Это был пример к изначальному обсуждению могут ли переупорядочиваться обычные и volatile обращения:
http://www.rsdn.ru/forum/java/3532424.1.aspx
Автор: Golovach Ivan
Дата: 10.09.09

Я хотел показать, что суть не в этом и это вообще не то, как нужно думать, и вообще говоря, могут переупорядочиваться (обычное обращение и volatile store) и/или (volatile load и обычное обращение). Другого способа объяснить результат в моём примере я не вижу.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[16]: volatile
От: remark Россия http://www.1024cores.net/
Дата: 10.10.09 14:49
Оценка:
Здравствуйте, Golovach Ivan, Вы писали:

GI>По моему мнению, тут небольшое недоразумение. В отличии от моделей памяти процессоров, где (как я понимаю) инструкции LFENCE, SFENCE, MFENCE, locking instructions, etc. имеют ordering-свойства НЕ ЗАВИСЯЩИЕ ОТ АДРЕССА в памяти


Есть разные модели. Например на IA-64 упорядочивание обеспечивается командами ld.acq/st.rel (load-acquire/store-release), что фактически соответствует современной модели принятой в C/C++/C#/Java.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[17]: volatile
От: denis.zhdanov Россия http://denis-zhdanov.blogspot.com/
Дата: 12.10.09 18:56
Оценка:
Здравствуйте, Golovach Ivan, Вы писали:

GI>Вы сказали "Если же считать, что мы работаем в окружении, где поток, отличный от того, в котором создан объект, всегда видит полностью инициализированный объект и может не увидеть только измененное значение поля, описанная ситуация невозможна.".

GI>А я утверждаю что согласно спеке между потоком запустившим main (и инициализировавшим shared, и явно создавшем два других потока) и двумя созданными потоками установлено отношение happend-before (17.4.4 Synchronization Order: "An action that starts a thread synchronizes-with the first action in the thread it starts."). А значит потоки видят только полностью сконструированный объект. А значит, согласно вашему утверждению такое поведение(вывод в консоль) невозможно.

Верно, согласно jls такая ситуация возможна.
http://denis-zhdanov.blogspot.com
Re[16]: volatile
От: denis.zhdanov Россия http://denis-zhdanov.blogspot.com/
Дата: 12.10.09 19:10
Оценка:
Здравствуйте, Golovach Ivan, Вы писали:

GI>Для обычных ничего не нашел. Детальный анализ гарантий для volatile немного загнал меня в тупик:

GI>JVM Spec. Edition 2. CHAPTER 8. Threads and Locks.[http://java.sun.com/docs/books/jvms/second_edition/html/Threads.doc.html]:
GI>"Variables are kept in a main memory that is shared by all threads."
GI>...

Некорректно рассматривать старую спецификацию. Правильная лежит здесь.
http://denis-zhdanov.blogspot.com
Re[17]: volatile
От: remark Россия http://www.1024cores.net/
Дата: 12.10.09 19:22
Оценка:
Здравствуйте, denis.zhdanov, Вы писали:

DZ>Здравствуйте, Golovach Ivan, Вы писали:


GI>>Для обычных ничего не нашел. Детальный анализ гарантий для volatile немного загнал меня в тупик:

GI>>JVM Spec. Edition 2. CHAPTER 8. Threads and Locks.[http://java.sun.com/docs/books/jvms/second_edition/html/Threads.doc.html]:
GI>>"Variables are kept in a main memory that is shared by all threads."
GI>>...

DZ>Некорректно рассматривать старую спецификацию. Правильная лежит здесь.


Некорректно давать ссылку на спецификацию JLS, когда речь шла о спецификации JVM


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[18]: volatile
От: denis.zhdanov Россия http://denis-zhdanov.blogspot.com/
Дата: 13.10.09 04:50
Оценка:
Здравствуйте, remark, Вы писали:

R>Некорректно давать ссылку на спецификацию JLS, когда речь шла о спецификации JVM


R>


Точно!
http://denis-zhdanov.blogspot.com
Re[19]: volatile
От: Golovach Ivan Украина http://kharkovitcourses.blogspot.com
Дата: 19.10.09 22:26
Оценка:
Здравствуйте, denis.zhdanov, Вы писали:

DZ>Здравствуйте, remark, Вы писали:


R>>Некорректно давать ссылку на спецификацию JLS, когда речь шла о спецификации JVM


R>>


DZ>Точно!


Согласен, мое цитирование некорректно.
Версия JVM Spec Ver2 уже устарела, а в третьей еще не написаны Самая Главная Глава №8: "Threads and Locks".
Вместо нее там стоит отсылка
"IN The Java™ Virtual Machine Specification, Second Edition, Chapter 8 detailed
the low-level actions that explained the interaction of Java virtual machine threads
with a shared main memory. It was adapted from Chapter 17 of The Java™ Language
Specification, First Edition.
Chapter 17 in The Java™ Language Specification, Third Edition, was updated
to incorporate The Java™ Memory Model and Thread Specification produced by
the JSR-133 Expert Group. The reader is referred to that chapter for information
about threads and locks."

JVM Spec ver3 == http://blogs.sun.com/abuckley/resource/JVMS3-DRAFT-20090512.zip
взято отсюда — http://blogs.sun.com/abuckley/entry/draft_of_the_java_vm
Это пока "DRAFT 2009-05-12"

Корректнее, видимо, опираться на http://unladen-swallow.googlecode.com/files/journal.pdf
Это прислал мне remark сюда — http://kharkovconcurrencygroup.blogspot.com/2009/10/java-memory-model.html.
За что я ему очень признателен.

P.S. С этим New JMM — вообще огромная проблема, нет единственного полного официального описания от SUN. Приходится опираться на статьи по всей сети непосредственно от авторов.

P.P.S. JLS Spec ver3 — не цитировал, так как там нет достаточного материала по volatile переменным.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.