Re[12]: вопрос по блокировке
От: Pavel Dvorkin Россия  
Дата: 14.02.12 14:56
Оценка:
Здравствуйте, ., Вы писали:

.>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>>Имеется в виду, что замена ссылки всегда атомарна, как утверждает Sun. Но если она не volatile, то это просто замена 4(8) байт. А если volatile, то (по-вашему) еще и сброс всего кеша всех тредов. В это я как-то плохо верю.

.>Не всех, а тех, которые обращаются к этой volatile переменной в данный момент.

Что значит "тех, которые обращаются " ? Обращается-то один. И зачем его кеш сбрасывать, когда надо сбрасывать кеш того потока, который по этой volatile ссылке что-то там модифицировал ?

>И не всего кеша, а тот который должен happens-before обращения к этой переменной.


Это как, можно узнать ? Кеш ядра прозрачен для программы, никто не знает, какая переменая где в кеше находится и есть ли она там вообще.
With best regards
Pavel Dvorkin
Re[15]: вопрос по блокировке
От: . Великобритания  
Дата: 14.02.12 14:58
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>С++ тут ни при чем. С таким же успехом можно писать на ассемблере, где локов вообще нет, или на Delphi, где их тоже нет. Как и в С++.

В ассемблере вообще-то специальные инструкции есть для барьеров. Но я тут не копенгаген.

PD>То есть ты утверждаешь что при любом обращении к synchronized переменной вся паять всех кешей сбрасывается в RAM ? Иного объяснения я не вижу. Знать, какой поток изменил данные по этой synchronized ссылке никто не может.

Сам поток при входе-выходе знает. Не понимаю зачем это знать другим потокам. Посмотри вот этот код:
int a=0, b=0;
...
a=1;
//synchronized(this){
b=1;
//}

если переменные non-volatile то если один поток прочитает b и там будет 1, то переменная a может иметь значение 0. Хотя это выглядит дико, но оптимизаторы/кеши имеют право.
Если убрать комментарии или переменную b объявить volatile, то спецификация языка java 1.5 гарантирует что a==1.

Всё абсолютно то же самое и в C++ c синхронизацией. Только есть разница для volatile — там гарантии тоже нет для некоторых компиляторов.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[15]: вопрос по блокировке
От: E.K. Великобритания  
Дата: 14.02.12 15:00
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:


EK>>Ну не меняется и отлично. Значит используйте synchronized. Я лишь пытался объяснить что volatile это не только memory consistency но и happens-before. Судя по вашим предыдущим комментариям вы этот момент упускаете.


PD>Я об этом и спрашиваю. Как и когда реализуется этот самый happen-before ? Кто его организует для всего графа, висящего на этой ссылке ?


Зависит от реализации VM.

PD>Обращение к volatile сбрасывает кеш всех ядер ? Да или нет ?


По спецификации обязано сбрасывать только кеш того потока который делает volatile-read и только того состояния которое было изменено до последнего volatile-write.


PD>Можно дать определение общего состояния ?


Мутабельное (изменяемое) состояние к которому могут иметь одновременный доступ (на запись или чтение) два и более потока исполнения.

PD>Когда и как оно синхронизируется.


Когда — при чтении или записи данных.

Как — synchronized, volatile или java.util.concurrent.lock.* в зависимости от задачи. Есть еще AtomicXXX классы которые обеспечивают доступ к общему состоянию без необходимости синхронизации
It is always bad to give advices, but you will be never forgiven for a good one.
Oscar Wilde
Re[12]: вопрос по блокировке
От: Pavel Dvorkin Россия  
Дата: 14.02.12 15:03
Оценка:
Здравствуйте, E.K., Вы писали:


PD>>То есть при обращении к volatile переменной сбрасываются все изменения в кэше всех потоков ? При любом обращении ?


EK>Только в том потоке в котором вы читаете. И только те изменения которые были сделаны до записи volatile.


Что в том потоке ? Он обращается к volatile переменной. Она ссылается на данные, изготовленные другим потоком. каким — бог знает. Поэтому данные в кеше неизвестно какого потока.


PD>>Аргументируйте. А если эта ссылка косвенно ссылается на 200 Мб всяких данных ?


EK>Значит все 200МБ будут видимы вашему потоку. Конкретная реализация JVM может вообще решить не использовать никакого кеша потока для этих данных — кто его знает? Главное — то, что вам гарантирует спецификация языка.


Что она опять же гарантирует ? Что при обращении к synchronized переменной все данные, доступные из нее, будут видны потоку, который выполнил это обращение ?

EK>>>Вообще не понял что имеется ввиду.


PD>>Имеется в виду, что замена ссылки всегда атомарна, как утверждает Sun. Но если она не volatile, то это просто замена 4(8) байт. А если volatile, то (по-вашему) еще и сброс всего кеша всех тредов. В это я как-то плохо верю.


EK>Не всего кеша и не всех потоков. Не выкручивайте ответы наизнанку.


Я ничьи ответы не выкручиваю. Я просто объясняю, что иначе это сделать невозможно. IMHO. Если считаете, что возможно — объясните как.

Может, я плохо понял, но там опять о самой ссылке, а не о том, на что она ссылается.
With best regards
Pavel Dvorkin
Re[16]: вопрос по блокировке
От: Pavel Dvorkin Россия  
Дата: 14.02.12 15:05
Оценка:
Здравствуйте, ., Вы писали:

>>Иного объяснения я не вижу. Знать, какой поток изменил данные по этой synchronized ссылке никто не может.

.>Сам поток при входе-выходе знает.

Я уже не знаю, как объяснять. Давай закончим.


.>Всё абсолютно то же самое и в C++ c синхронизацией. Только есть разница для volatile — там гарантии тоже нет для некоторых компиляторов.


Я об одном, ты совсем о другом. Мы друг друга не понимаем.
With best regards
Pavel Dvorkin
Re[13]: вопрос по блокировке
От: . Великобритания  
Дата: 14.02.12 15:06
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Что в том потоке ? Он обращается к volatile переменной. Она ссылается на данные, изготовленные другим потоком. каким — бог знает. Поэтому данные в кеше неизвестно какого потока.

Он обращается к значению volatile переменной, которая была до этого записана другим потоком. Когда другой поток записывал, он сбросил все свои кеши, которые были happens-before. Где чего неизвестно??
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[7]: вопрос по блокировке
От: StanislavK Великобритания  
Дата: 14.02.12 15:07
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

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


PD>>>Для уточнения — такой вопрос. Пусть я в одном потоке записал элементы некоего 100 Кб массива. Что я должен сделать, чтобы другой поток гарантированно увидел эти данные ?

SK>>Общее правило такое — изменения флешатся на записи в volatile и на освобождении лока.
PD>Этого мало.
Мало чего?

PD>Они должны флешиться при чтении volatile переменной, причем для всех потоков (точнее, всех кешей всех ядер).

Кому должны? Флешиться она должны так как описано в спеке и не более. И не понятно, как соотносятся "все потоки" и "все ядра".

PD>Пусть имеем volatile Clazz c; Поток T1 под нее выделил new Clazz и в него что-то записал, может, в класс X, доступный через Clazz через 5 промежуточных классов. Эти данные то ли в RAM, то ди в кеще (working storage) потока T1.

PD>Поток T2 обращается к c. Обращается, а не меняет ее! После этого он по ней полезет в X. При этом должны сброситься в кеш все изменения в Clazz, X и всех 5 промежуточных классах. Но обходить граф никто не будет, да и смысла нет. Значит, должен сбрасываться весь кеш. А поскольку при обращении к c неизвестно, какой поток до этого имел с ней дело, кеш надо сбрасываать у всех ядер.

Чтение volatile гарантирует, что произойдет "запись" обратно из "главной памяти" в "память птока". Запись гарантирует обраное — "запись" из "память потока" в "главную память".
В описаном сценарии получается, что когда поток T1 записал в volatile, то его пямять флешнулась в "главную память". Если он после этого делал изменения, то не факт, что они будут в "главной памяти".
Когда T2 обращается к volatile, то гарантируется, что он увидит то, что было в "главной памяти" на момент обращания.

PD>Так ?

Не уверен. Не понимаю при чем тут все ядра.
Re[16]: вопрос по блокировке
От: Pavel Dvorkin Россия  
Дата: 14.02.12 15:07
Оценка:
Здравствуйте, E.K., Вы писали:

Ладно, давай закончим. Мы друг друга не понимаем. Мы о разных вещах говорим.
With best regards
Pavel Dvorkin
Re[13]: вопрос по блокировке
От: E.K. Великобритания  
Дата: 14.02.12 15:10
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:


PD>Может, я плохо понял, но там опять о самой ссылке, а не о том, на что она ссылается.


По ссылке именно об эффекте volatile на состояние и видимость других переменных. Почитайте внимательно.
It is always bad to give advices, but you will be never forgiven for a good one.
Oscar Wilde
Re[17]: вопрос по блокировке
От: . Великобритания  
Дата: 14.02.12 15:14
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

>>>Иного объяснения я не вижу. Знать, какой поток изменил данные по этой synchronized ссылке никто не может.

.>>Сам поток при входе-выходе знает.
PD>Я уже не знаю, как объяснять. Давай закончим.
Ты по-моему на процесс с другой стороны смотришь вот и странным кажется. Там всё наоборот. Не поток "вытягивает" данные из других потоков, а наоборот, поток помещает/забирает данные в/из разделяемую память в точно обозначенные кодом моменты времени.

.>>Всё абсолютно то же самое и в C++ c синхронизацией. Только есть разница для volatile — там гарантии тоже нет для некоторых компиляторов.

PD>Я об одном, ты совсем о другом. Мы друг друга не понимаем.
Ну ладно, не знаю тогда что не ясно. Вроде всё просто. Видимо ты не до конца понимаешь как оно всё работает и в С++. Почитай литературу, там всё более чётко объясняется.
Если что, показывай пример кода, помедитируем.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[8]: вопрос по блокировке
От: Pavel Dvorkin Россия  
Дата: 14.02.12 15:17
Оценка:
Здравствуйте, StanislavK, Вы писали:

PD>>Пусть имеем volatile Clazz c; Поток T1 под нее выделил new Clazz и в него что-то записал, может, в класс X, доступный через Clazz через 5 промежуточных классов. Эти данные то ли в RAM, то ди в кеще (working storage) потока T1.

PD>>Поток T2 обращается к c. Обращается, а не меняет ее! После этого он по ней полезет в X. При этом должны сброситься в кеш все изменения в Clazz, X и всех 5 промежуточных классах. Но обходить граф никто не будет, да и смысла нет. Значит, должен сбрасываться весь кеш. А поскольку при обращении к c неизвестно, какой поток до этого имел с ней дело, кеш надо сбрасываать у всех ядер.

SK>Чтение volatile гарантирует, что произойдет "запись" обратно из "главной памяти" в "память птока". Запись гарантирует обраное — "запись" из "память потока" в "главную память".


SK>В описаном сценарии получается, что когда поток T1 записал в volatile, то его пямять флешнулась в "главную память".


Так, допустим.

>Если он после этого делал изменения, то не факт, что они будут в "главной памяти".


Тоже допустим.

SK>Когда T2 обращается к volatile, то гарантируется, что он увидит то, что было в "главной памяти" на момент обращания.


Резюме.

Поток Т1 записал в volatile, его память переписана в RAM. Он потом делал изменения по этой ссылке в классе X, но не менял саму ссылку (то есть не менял volatile переменную, а менял данные, по ней доступные по ссылке). Поток T2 обратился к этим данным и увидел то, что было до этих изменений. (может, конечно, и изменения увидел, но не гарантируется)

Я ничего не переврал ? Вроде нет.

Тогда возвращаемся к исходному вопросу в моем примере. Как мне гарантировать, что изменения в классе X, которые он сделал после изменения volatile переменной, будут видны T2 ?
Более того, как мне гарантировать, что изменения в классе X, которые он сделал, в случае , если он вообще не менял саму ссылку c , будут видны потоку T2 ?

PD>>Так ?

SK>Не уверен. Не понимаю при чем тут все ядра.

Попробуй ответить на мой вопрос, поймешь
With best regards
Pavel Dvorkin
Re[18]: вопрос по блокировке
От: Pavel Dvorkin Россия  
Дата: 14.02.12 15:20
Оценка:
Здравствуйте, ., Вы писали:

.>Если что, показывай пример кода, помедитируем.


Помедитируй на моем примере с массивом и попробуй объяснить (себе) как изменения в нем окажутся видны первому потоку и почему. Ссылка там одна-единственная на все про все.
With best regards
Pavel Dvorkin
Re[9]: вопрос по блокировке
От: . Великобритания  
Дата: 14.02.12 15:23
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Я ничего не переврал ? Вроде нет.

Да. Ок.

PD>Тогда возвращаемся к исходному вопросу в моем примере. Как мне гарантировать, что изменения в классе X, которые он сделал после изменения volatile переменной, будут видны T2 ?

PD>Более того, как мне гарантировать, что изменения в классе X, которые он сделал, в случае , если он вообще не менял саму ссылку c , будут видны потоку T2 ?
Залочить оба треда на одном локе перед изменением и перед чтением этих данных.
При освобождении лока пишуший тред сбросит все happens-before данные в главную память и читающий тред при взятии лока прочтёт их из главной памяти.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[19]: вопрос по блокировке
От: . Великобритания  
Дата: 14.02.12 15:29
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

.>>Если что, показывай пример кода, помедитируем.

PD>Помедитируй на моем примере с массивом и попробуй объяснить (себе) как изменения в нем окажутся видны первому потоку и почему. Ссылка там одна-единственная на все про все.
Без локов — могут не оказаться!
Чтобы гарантированно оказались видны ты обязан использовать lock. твой sleep(10000) вообще ничего не гарантирует.

Ещё как вариант (только если ты хочешь избежать синхронизации использованием lock-free для улучшение перформанса) — объявить все меняемые данные как volatile, но это невозможно для элементов массива, поэтому если только смотреть в сторону AtomicIntegerArray.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[10]: вопрос по блокировке
От: Pavel Dvorkin Россия  
Дата: 14.02.12 15:33
Оценка:
Здравствуйте, ., Вы писали:

PD>>Тогда возвращаемся к исходному вопросу в моем примере. Как мне гарантировать, что изменения в классе X, которые он сделал после изменения volatile переменной, будут видны T2 ?

PD>>Более того, как мне гарантировать, что изменения в классе X, которые он сделал, в случае , если он вообще не менял саму ссылку c , будут видны потоку T2 ?
.>Залочить оба треда на одном локе перед изменением и перед чтением этих данных.
.>При освобождении лока пишуший тред сбросит все happens-before данные в главную память и читающий тред при взятии лока прочтёт их из главной памяти.

Вот это понятно. Логично. Если лочить код, исполняемый потоком, то при выходе из лока он может сбросить кеш в ОП. Аналоги в Win32 — Release(Mutex, Semaphor), LeaveCriticalSection, SetEvent...

То есть объявление synchronized ссылки для этой памяти ни чего в этом плане не дает ? Здесь же нет кода. Доступ к этой переменной был давным-давно, изменения вносились после доступа.
With best regards
Pavel Dvorkin
Re[20]: вопрос по блокировке
От: Pavel Dvorkin Россия  
Дата: 14.02.12 15:38
Оценка:
Здравствуйте, ., Вы писали:

.>Без локов — могут не оказаться!

.>Чтобы гарантированно оказались видны ты обязан использовать lock. твой sleep(10000) вообще ничего не гарантирует.

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

.>Ещё как вариант (только если ты хочешь избежать синхронизации использованием lock-free для улучшение перформанса) — объявить все меняемые данные как volatile, но это невозможно для элементов массива, поэтому если только смотреть в сторону AtomicIntegerArray.


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

(Про мэпы сказал просто для примера, не надо рассказывать по конкурентные мэпы
With best regards
Pavel Dvorkin
Re[11]: вопрос по блокировке
От: . Великобритания  
Дата: 14.02.12 15:38
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>То есть объявление synchronized ссылки для этой памяти ни чего в этом плане не дает ? Здесь же нет кода. Доступ к этой переменной был давным-давно, изменения вносились после доступа.

"synchronized"? Не понял. Код давай. syncrhronized и есть Aquire/Release пара.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[12]: вопрос по блокировке
От: Pavel Dvorkin Россия  
Дата: 14.02.12 15:45
Оценка:
Здравствуйте, ., Вы писали:

.>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>>То есть объявление synchronized ссылки для этой памяти ни чего в этом плане не дает ? Здесь же нет кода. Доступ к этой переменной был давным-давно, изменения вносились после доступа.

.>"synchronized"? Не понял. Код давай. syncrhronized и есть Aquire/Release пара.

Держи


class A
{
  synchronized int[] a = new int[1000];
  int s;
  void main()
{
 Thread2.start();
 f();
 Thread2.join();
}

  void f() // вызывается из mainThread
{ 
 for (int i = 0; i < 1000; i++)
  a[i] = i;

// поток mainThread изменил память, но, возможно, эти изменения только в его кеше, а не в RAM.
}

int sum() // вызывается из Thread2
{
 Thread.sleep(10000);
 // поток Thread2 обращается к этой памяти, но, возможно, ее содержимое неверно.
 for (int i = 0; i < 1000; i++)
  s+=i;
}


synchronized ссылка есть. Менять ее никто не меняет. Меняют память по ней. Код, меняющий память, не запрашивает лока явно.
With best regards
Pavel Dvorkin
Re[13]: поправка
От: Pavel Dvorkin Россия  
Дата: 14.02.12 15:46
Оценка:
Вместо

PD> for (int i = 0; i < 1000; i++)

PD> s+=i;

читать

PD> for (int i = 0; i < 1000; i++)

PD> s+=a[i];
With best regards
Pavel Dvorkin
Re[21]: вопрос по блокировке
От: . Великобритания  
Дата: 14.02.12 15:46
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

.>>Без локов — могут не оказаться!

.>>Чтобы гарантированно оказались видны ты обязан использовать lock. твой sleep(10000) вообще ничего не гарантирует.
PD>Вот теперь ясно. syncronized переменная ничего не гарантирует в этом плане, нужен лок. Не для блокировки чего-то, а для сброса кеша при выходе из него.
synchronized(obj) {
 f();
}

логически эквивалентно такому:
obj.$monitor.lock();
try {
  f();
}
finally {
  obj.$monitor.unlock();
}

, где ($monitor — скрытая структура JVM которая есть у каждого объекта в яве).
т.е. я не понял твой вопрос, т.к. ты видимо не понимаешь что делает synchronized. или ты хотел сказать volatile?

PD>Ну массив здесь только для примера. А если это ссылка не на массив, а на развесистое дерево (граф) экземпляров классов ? Например, ссылка на экземпляр класса, в котором мэп из бог знает чего, а это бог знает что содержит мэп бог знает чего еще раз. Не могу же я все лочить, да и нет у меня доступа к внутренним полям этих мэпов.

PD>(Про мэпы сказал просто для примера, не надо рассказывать по конкурентные мэпы
Ты можешь лочить все обращения к этой развесистой структуре.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.