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

PD>Держи

PD>
PD>  synchronized int[] a = new int[1000];
PD>

Это оффтопик, т.к. не ява.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[22]: вопрос по блокировке
От: Pavel Dvorkin Россия  
Дата: 14.02.12 15:48
Оценка:
Здравствуйте, ., Вы писали:

.>
.>synchronized(obj) {
.> f();
.>}
.>

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

.>, где ($monitor — скрытая структура JVM которая есть у каждого объекта в яве).

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

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


PD>>Держи

PD>>
PD>>  synchronized int[] a = new int[1000];
PD>>

.>Это оффтопик, т.к. не ява.

Тьфу. Рехнулся я с этими synchronized

volatile int[] a = new int[1000];
With best regards
Pavel Dvorkin
Re[9]: вопрос по блокировке
От: StanislavK Великобритания  
Дата: 14.02.12 15:53
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

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

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

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

PD>Более того, как мне гарантировать, что изменения в классе X, которые он сделал, в случае , если он вообще не менял саму ссылку c , будут видны потоку T2 ?
Это вот этот?

class A
{
  int[] a = new int[1000];
  int s;

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

int sum() // вызывается из Thread2
{
 for (int i = 0; i < 1000; i++)
  s+=i;
}


Не совсем очевидно, но тут все будет хорошо. Thread.start имеет happens-before семантику — "память потока" потока, который вызывает Thread2.start() будет "записана" в общую память. Про это тут: http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html "Memory Consistency Properties". Но в общем, в таких моментах рекомендуется делать синхронизацию, исключительно из сообращений "читабельности".

PD>>>Так ?

SK>>Не уверен. Не понимаю при чем тут все ядра.
PD>Попробуй ответить на мой вопрос, поймешь
Все равно не понял.
Re[15]: вопрос по блокировке
От: . Великобритания  
Дата: 14.02.12 15:54
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Тьфу. Рехнулся я с этими synchronized

Тихо шифером шурша...

PD> volatile int[] a = new int[1000];

Тогда этот код может глючить, не потокобезопасный. А volatile тут ни на что не влияет. И всё в точности как и в С++.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[10]: вопрос по блокировке
От: Pavel Dvorkin Россия  
Дата: 14.02.12 16:02
Оценка:
Здравствуйте, StanislavK, Вы писали:

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


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

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

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

PD>>Более того, как мне гарантировать, что изменения в классе X, которые он сделал, в случае , если он вообще не менял саму ссылку c , будут видны потоку T2 ?
SK>Это вот этот?

Лучше этот


class A
{
  volatile 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;
}

int sum() // вызывается из Thread2
{
 Thread.sleep(10000);
 for (int i = 0; i < 1000; i++)
  s+=i;
}


Здесь Thread.start вызывается намного раньше всех этих действий. Поток main расписывает массив, поток Thread2 хочет брать оттуда данные. synchronized нет, volatile есть, но она тут ничего не делает, так как эта ссылка не меняется.

Данные будут корректные ?
With best regards
Pavel Dvorkin
Re[16]: вопрос по блокировке
От: Pavel Dvorkin Россия  
Дата: 14.02.12 16:03
Оценка:
Здравствуйте, ., Вы писали:

.>Тогда этот код может глючить, не потокобезопасный. А volatile тут ни на что не влияет. И всё в точности как и в С++.


Резюме — volatile в плане синхронизации данных по этой ссылке ничего не дает, а дает только атомарность изменения самой ссылки. synchronized и все его аналоги дают. Так ?
With best regards
Pavel Dvorkin
Re[11]: вопрос по блокировке
От: StanislavK Великобритания  
Дата: 14.02.12 16:16
Оценка: 18 (1)
Здравствуйте, Pavel Dvorkin, Вы писали:

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


PD>Лучше этот

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

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

PD>int sum() // вызывается из Thread2
PD>{
PD> Thread.sleep(10000);
PD> for (int i = 0; i < 1000; i++)
PD>  s+=i;
PD>}
PD>


PD>Здесь Thread.start вызывается намного раньше всех этих действий. Поток main расписывает массив, поток Thread2 хочет брать оттуда данные. synchronized нет, volatile есть, но она тут ничего не делает, так как эта ссылка не меняется.

PD>Данные будут корректные ?
Если внимательно посмотреть, то тут все будет пучком, т.к. потоки не шарят данные
Подозреваю, что "s+=i", это на самом деле "s+=a[i]". В этом случае без дополнительной синхронизации никаких гарантий нет.
Re[17]: вопрос по блокировке
От: . Великобритания  
Дата: 14.02.12 16:16
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

.>>Тогда этот код может глючить, не потокобезопасный. А volatile тут ни на что не влияет. И всё в точности как и в С++.

PD>Резюме — volatile в плане синхронизации данных по этой ссылке ничего не дает, а дает только атомарность изменения самой ссылки. synchronized и все его аналоги дают. Так ?
Не даёт. Атомарность тут не причём (все ссылки и так атомарны). synchronized дают, ценой ухудшения производительности.

Разница volatile/non-volatile для данного случая проявится например в такой ситуации:
class C
{
  /*volatile*/ int []a;
  void f()
  {
    int []a = new int[1000];
    for (int i = 0; i < 1000; i++)
     a[i] = i;
    this.a = a;
  }
}

Тут наличие volatile — обязательно для консистентности данных.

ЗЫЖ У меня появился вопрос к знатокам. В коде, который привёл Павел, второй тред гарантировано увидит 999 элементов, т.к. для записи i-го элемента берётся ссылка на массив из volatile переменной. Значит при чтении этой переменной он должен записать в главную память предыдущий элемент. Последний элемент же может остаться в кеше, т.к. после его записи не происходит обращений к volatile-переменным. Я правильно понимаю? Или чтение volatile-переменной не гарантирует happens-before запись ячейки массива?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[12]: вопрос по блокировке
От: Pavel Dvorkin Россия  
Дата: 14.02.12 16:19
Оценка:
Здравствуйте, StanislavK, Вы писали:

SK>Подозреваю, что "s+=i", это на самом деле "s+=a[i]".


Да, конечно.

>В этом случае без дополнительной синхронизации никаких гарантий нет.


Ясно. А для нее достаточно войти в какой-нибудь лок, тогда при выходе из него Thread2 сбросит кэш.

Спасибо.
With best regards
Pavel Dvorkin
Re[13]: вопрос по блокировке
От: . Великобритания  
Дата: 14.02.12 16:24
Оценка: 18 (1) +1
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Ясно. А для нее достаточно войти в какой-нибудь лок, тогда при выходе из него Thread2 сбросит кэш.

Не достаточно! Ещё необходимо, чтобы другой тред тоже вошел в тот же лок, чтобы он перегрузил свой кеш из сброшенного в главную память другого кеша.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[13]: вопрос по блокировке
От: StanislavK Великобритания  
Дата: 14.02.12 16:38
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

SK>>Подозреваю, что "s+=i", это на самом деле "s+=a[i]".

PD>Да, конечно.

>>В этом случае без дополнительной синхронизации никаких гарантий нет.

PD>Ясно. А для нее достаточно войти в какой-нибудь лок, тогда при выходе из него Thread2 сбросит кэш.
лушче покажите на примере кода, я так не понимаю. слишком много возможностей для интерпретации этой фразы.
Re: вопрос по блокировке
От: E.K. Великобритания  
Дата: 14.02.12 17:08
Оценка: 18 (1)
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Хотелось бы знать мнение по этому вопросу, как лучше сделать.


Вот смотрю я на весь этот тред и вижу несколько вещей:

1) вы упорно пытаетесь все свести к модели C++ хотя и языки и система поддержки многопоточности и платформы разные. Программы написанные на Java исполняются на виртуальной машине а не голом железе — о каких ядрах и проч. тут может идти речь? Виртуальная машина вправе реализовывать кеш потока так как ей вздумается — используя внутренние словари, функции ОС и т.п. Java вам может дать лишь определенные гарантии относительно порядка выполнения операций и видимости изменений но не говорит как все это должно быть реализовано.

2) Применительно к вашей задаче уже много разных людей дали по сути один и тот же ответ только в разной форме. Причем по несколько раз. Если вам просто задачу решить, так это уже много раз показали как это сделать, с примерами кода. Если вам важнее разобраться, понять какова модель памяти в Java, тут нужно курить доки и спеки, посколько очевидно на пальцах объяснить не получается. Пока не будет твердого понимания что такое memory consistency, happens-before, syncrhonization-order, total-order vs. local-order, а также как на них влияют применение volatile, syncrhonized и др. примитивов синхронизации тут можно долго и бесполезно спорить. Нужно читать.

Ссылки уже давали, приведу опять:

Если нужна помощь с переводом — обращайтесь.
It is always bad to give advices, but you will be never forgiven for a good one.
Oscar Wilde
Re[14]: вопрос по блокировке
От: Pavel Dvorkin Россия  
Дата: 15.02.12 03:13
Оценка:
Здравствуйте, ., Вы писали:

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


PD>>Ясно. А для нее достаточно войти в какой-нибудь лок, тогда при выходе из него Thread2 сбросит кэш.

.>Не достаточно! Ещё необходимо, чтобы другой тред тоже вошел в тот же лок, чтобы он перегрузил свой кеш из сброшенного в главную память другого кеша.

Да, согласен.

Спасибо.
With best regards
Pavel Dvorkin
Re[2]: вопрос по блокировке
От: Pavel Dvorkin Россия  
Дата: 15.02.12 03:23
Оценка:
Здравствуйте, E.K., Вы писали:

EK>1) вы упорно пытаетесь все свести к модели C++ хотя и языки и система поддержки многопоточности и платформы разные.


Это не совсем так. Модели С++ не существует, есть модель подлежащей платформы. Нет никаких различий, писать на С++ или на ассемблере или Delphi (нативной). Меня как раз интересовало, как сопрягается Ява с подлежащей платформой, то есть ОС и работой процессора.


>Программы написанные на Java исполняются на виртуальной машине а не голом железе — о каких ядрах и проч. тут может идти речь?


Ядра никуда не денутся, потому что они есть. И потоки тоже. Работа кеша ядер не зависит от того, есть тут Java VM или .NET VM или что-то иное. С точки зрения Windows Java VM есть просто приложение, вполне обычное (потому что никаких необычных приложений нет), а с точки зрения процессора и ядер даже не важно, работает тут Windows или Linux или еще что-то. Потоки больше зависят от ОС, но в пределах ОС потоки есть потоки, исполняются ли они в Java VM или в моей сделанной на С++ программе. Кстати, Java VM есть всего лишь программа, написанная на С++

>Виртуальная машина вправе реализовывать кеш потока так как ей вздумается — используя внутренние словари, функции ОС и т.п.


Это она может сделать с памятью, которую она сама отведет для потока внутри себя. Над кешем процессора она не властна.


EK>Если нужна помощь с переводом — обращайтесь.


Спасибо, проблем тут нет.
With best regards
Pavel Dvorkin
Re[3]: вопрос по блокировке
От: maxkar  
Дата: 16.02.12 07:48
Оценка: 9 (1)
Здравствуйте, Pavel Dvorkin, Вы писали:

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


EK>>1) вы упорно пытаетесь все свести к модели C++ хотя и языки и система поддержки многопоточности и платформы разные.


PD>Это не совсем так. Модели С++ не существует, есть модель подлежащей платформы. Нет никаких различий, писать на С++ или на ассемблере или Delphi (нативной). Меня как раз интересовало, как сопрягается Ява с подлежащей платформой, то есть ОС и работой процессора.


Есть разные модели! Есть. Java Memory Model может быть различными способами сопряжена с платформой. Например, volatile не гарантирует сброс кэша. В частности, если вирутальная машина видит, что ссылки на объект никогда не размещались в куче (а только на стеке), то она может пропускать операции "сбороса/чтения" кэша. Называется lock elision (в гугле информация есть). В 6-ке эта техника вроде бы не реализована, но в будущих версиях вполне может быть введена без какого-либо уведомления. Отсюда и требование синхронизации на одном и том же объекте.

>>Программы написанные на Java исполняются на виртуальной машине а не голом железе — о каких ядрах и проч. тут может идти речь?


PD>Работа кеша ядер не зависит от того, есть тут Java VM или .NET VM или что-то иное.

Работа ядер не зависит. А вот модель языка может быть по-разному отображена на операции с кэшем (сброс кэша). Кроме того, Java подразумевает работу на нескольких архитектурах. Например, на NUMA, где понятия "общей памяти" может не быть как таковой. В этом случае обращение к volatile (и любое другое Happens-before отношение) — загрузка выгрузка только изменившихся страниц. Или "синхронизация" рабочей памяти с тем процессором, который в последний раз приобретал блокировку. Отсюда опять требование синхронизации на одном и том же объекте.

И вообще, модель с "записью в общую память" не очень хорошая. Она была правильна до версии 1.4 включительно (и в ее терминах формулировалась модель памяти в JLS 2-nd edition). В третьей версии от понятия "общая память" полностью отказались и перевели все на happens-before. Сама по себе "общая память" гарантирует "монотонность" значений (если мы увидели какое-то значение, мы больше не можем увидеть более старое). Happens-before — не гарантирует (естественно, при остутствии синхронизации). Обсуждали здесь
Автор: maxkar
Дата: 15.10.10


PD>Это она может сделать с памятью, которую она сама отведет для потока внутри себя. Над кешем процессора она не властна.

Ну да. Но ведь вы будете видеть как раз то, что виртуальная машина сделала с памятью потока! А кэши процессора вы видите только "косвенно", как особенность реализации JMM на конкретной платформе.
Re[14]: вопрос по блокировке
От: StanislavK Великобритания  
Дата: 16.02.12 20:13
Оценка:
Здравствуйте, ., Вы писали:

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


PD>>Ясно. А для нее достаточно войти в какой-нибудь лок, тогда при выходе из него Thread2 сбросит кэш.

.>Не достаточно! Ещё необходимо, чтобы другой тред тоже вошел в тот же лок, чтобы он перегрузил свой кеш из сброшенного в главную память другого кеша.
Идеологически оно, конечно, верно. Но на практике не правда. Не нужен тот же лок.
Re[5]: вопрос по блокировке
От: StanislavK Великобритания  
Дата: 09.03.12 22:36
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

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



SK>>Так, что вот.. многопоточность, она на самом деле много хуже, чем кажется и читайте Java Memory Model и JVM spec про потоки, там еще много чего написано.

PD>Да, наверное ты прав. Я с потоками имел дело не раз на С++. Там нет необходимости учитывать этот memory barrier, так как примитивы синхронизации Win API обеспечивают запись измененных данных в RAM. Но там нет никакой working memory у потока (хотя кэш процессора, конечно, есть). Если в Яве ввели еще некую локальную память для потока, то понятно, что необходимо обеспечить ее синхронизацию с основной памятью.
PD>Для уточнения — такой вопрос. Пусть я в одном потоке записал элементы некоего 100 Кб массива. Что я должен сделать, чтобы другой поток гарантированно увидел эти данные ?

Нарвался тут в инете на замечательную статью про С++, там все популярно рассказано насчет того для чего нужны барьеры и какие проблемы могут вознукнуть при re-ordering.

http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf

Хотя статья про С++, настоятельно рекумендую для прочтения и тем кто пишет на яве, т.к. проблемы все те же.
Кстати, что интересно, я заметил, что программисты на С++ гораздо менее знакомы с правилами хорошего тона многопоточного программирования, чем программисты на яве. Во всяком случае это касается тех с кем я общаюсь, что ни в коей мере не является статистически значимой выборкой Связанно это с тем (опять же по-моим наблюдениям), что на С++ потоков стараются избегать и не без причины.
Re[6]: вопрос по блокировке
От: Pavel Dvorkin Россия  
Дата: 10.03.12 02:05
Оценка:
Здравствуйте, StanislavK, Вы писали:

SK>Хотя статья про С++, настоятельно рекумендую для прочтения и тем кто пишет на яве, т.к. проблемы все те же.

SK>Кстати, что интересно, я заметил, что программисты на С++ гораздо менее знакомы с правилами хорошего тона многопоточного программирования, чем программисты на яве. Во всяком случае это касается тех с кем я общаюсь, что ни в коей мере не является статистически значимой выборкой Связанно это с тем (опять же по-моим наблюдениям), что на С++ потоков стараются избегать и не без причины.

За ссылку спасибо, посмотрю, а вот утверждение насчет того, что на С++ потоков стараются избегать — странное. Я их неоднократно использовал. Просто там все эти проблемы с memory barrier скрыты в примитивах ОС, ну, а, конечно, никаких эффектов, связанных с работой JVM, нет в природе.
With best regards
Pavel Dvorkin
Re[7]: вопрос по блокировке
От: . Великобритания  
Дата: 10.03.12 11:56
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>За ссылку спасибо, посмотрю, а вот утверждение насчет того, что на С++ потоков стараются избегать — странное. Я их неоднократно использовал. Просто там все эти проблемы с memory barrier скрыты в примитивах ОС, ну, а, конечно, никаких эффектов, связанных с работой JVM, нет в природе.

Да всё то же самое. Только вместо эффектов JVM — эффекты оптимизатора. А проблем меньше, т.к. примитивы ОС, как я помню по твоим другим мессагам, WinAPI, а значит только одна операционка и одна железная платформа. В отличие от многоплатформенной явы.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.