powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Java [игнор отключен] [закрыт для гостей] / Товарищ не понимает
17 сообщений из 17, страница 1 из 1
Товарищ не понимает
    #34403104
02
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
02
Гость
Добрый день
Помогите, пожалуйста, разобраться с примером из книги Brian Goetz "Java Concurrency in Practice"

В главе 3.5 Safe Publication он пишет

You cannot rely on the integrity of partially constructed objects.
An observing thread could see the object in an inconsistent state, and then later see its state suddenly change, even though it has not been modified since publication.
In fact, if the Holder in Listing 3.15 is published using the unsafe publication idiom in Listing 3.14,
and a thread other than the publishing thread were to call assertSanity, it could throw AssertionError!

и далее...

...thread may see a stale value the first time it reads a field and then a more up-to-date value the next time, which is why assertSanity can throw AssertionError

Listing 3.14
Код: plaintext
1.
2.
3.
4.
5.
6.
	// Unsafe publication
	 public  Holder holder;

	 public   void  initialize() {
	    holder =  new  Holder( 42 );
	}
Listing 3.15
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
	 public   class  Holder {
	     private   int  n;

	     public  Holder( int  n) {  this .n = n; }

	     public   void  assertSanity() {
	         if  (n != n)
	             throw   new  AssertionError("This statement is false.");
	    }
	}

То есть, если я правильно понял, какой-нибудь поток вызывает holder.assertSanity(), читает левое n==42,
прерывается, а потом читает правое n, то оно уже может не быть 42
Как такое может случиться с классом Holder в том виде как он написан в Listing 3.15?

Большое спасибо
...
Рейтинг: 0 / 0
Товарищ не понимает
    #34403163
Фотография Timm
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
02...
Как такое может случиться с классом Holder в том виде как он написан в Listing 3.15?

Большое спасибо
Он как раз и написан так чтобы это могло случиться :) в том и смысл, что unsafe publication может приводить к подобным казусам. Разные потоки в такой ситуации в разные моменты времени могут видеть все что угодно. Подробности в JSR-133 (Java memory model).
...
Рейтинг: 0 / 0
Товарищ не понимает
    #34403252
02
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
02
Гость
Timm
В соответствии с JMM, по-моему разумению, с точки зрения какого-нибудь потока
n может может быть равным любому из значений, записанных в это n другими потоками
Но в данном случае, кто может что-нибудь записать в n после initialize()?
...
Рейтинг: 0 / 0
Товарищ не понимает
    #34403401
02
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
02
Гость
Даже если какой-то поток влезет между чтением левого n и правого n и скажет holder= new Holder(43), это же не изменит правое n?
...
Рейтинг: 0 / 0
Товарищ не понимает
    #34403506
Фотография Timm
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
02Timm
В соответствии с JMM, по-моему разумению ...
Но в данном случае, кто может что-нибудь записать в n после initialize()?
У JVM другое разумение.
Фокус не в том что кто то еще может записать туда, а в том что поток может не увидеть эти изменения сразу, и нет никаких гарантий на то какое значение в какой момент времени читающий поток будет видеть. Так понятно?
В книжке чуть ниже ведь написано:
Because synchronization was not used to make the Holder visible to other threads, we say the Holder was not properly published. Two things can go wrong with improperly published objects. Other threads could see a stale value for the holder field, and thus see a null reference or other older value even though a value has been placed in holder. But far worse, other threads could see an up-todate value for the holder reference, but stale values for the state of the Holder.[16] To make things even less predictable, a thread may see a stale value the first time it reads a field and then a more up-to-date value the next time, which is why assertSanity can throw AssertionError.
...
Рейтинг: 0 / 0
Товарищ не понимает
    #34403565
wessen
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Есть например поток А, он вызвал метод initialize() и отработал его от начала и до конца, затем запускается поток В и вызывает метод assertSanity(), доходит то места, где считывает первую переменную n, затем прерывается потоком C, который вызывает метод initialize(), отработал его от начала и до конца, т.е. создает новый объект holder. Затем управление передается опять потоку B и получается так, что он находится внутри метода объекта, которого нет и об этом потоку B никак не сообщается и продолжает читать вторую n, в которой может быть все что угодно.
...
Рейтинг: 0 / 0
Товарищ не понимает
    #34403721
02
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
02
Гость
Есть такие люди тупые и надоедливые
Так это я
Тимм
Если поток А инициализирует holder, до этого еще не инициализированную, то поток В, намеревающийся вызвать holder.assertSanity(), или обламывается, если к этому моменту holder все еще ==null, или видит в holder все, что сделал поток А. Ведь нельзя же в потоке В увидеть holder != null, ссылающийся на не инициализированный или частично инициализированный обьект
Holder?


wessen
Затем управление передается опять потоку B и получается так, что он находится внутри метода объекта, которого нет и об этом потоку B никак не сообщается и продолжает читать вторую n, в которой может быть все что угодно.

Почему же это которого нет. Он есть и на него указывает this, то есть та ссылка, на которой assertSanity() в потоке В был вызван???
...
Рейтинг: 0 / 0
Товарищ не понимает
    #34403796
Фотография Timm
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
02Есть такие люди тупые и надоедливые
Так это я
Тимм
Если поток А инициализирует holder, до этого еще не инициализированную, то поток В, намеревающийся вызвать holder.assertSanity(), или обламывается, если к этому моменту holder все еще ==null, или видит в holder все, что сделал поток А. Ведь нельзя же в потоке В увидеть holder != null, ссылающийся на не инициализированный или частично инициализированный обьект
Holder?

Можно!! Варианты:
1. holder == null
2. holder != null && n = 0
3. holder != null && n = 42
4. holder != null && n = <что то еще>
+ состояние объекта при чтении в разные моменты времени может быть разным.

Первые три случая могут быть. Насчет четвертого я не уверен.
...
Рейтинг: 0 / 0
Товарищ не понимает
    #34403842
02
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
02
Гость
Time-out
Надо подумать
...
Рейтинг: 0 / 0
Товарищ не понимает
    #34403872
wessen
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
02
Почему же это которого нет. Он есть и на него указывает this, то есть та ссылка, на которой assertSanity() в потоке В был вызван???

В том то и дело, что поток С вызвав метод initialize() переписал эту ссылку, и та область данных(объект handle, который создал поток А), которую все еще использует поток В, объявлена как неиспользованая и ее в любой момент может занять любой другой поток.
...
Рейтинг: 0 / 0
Товарищ не понимает
    #34404127
02
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
02
Гость
wessen
В том то и дело, что поток С вызвав метод initialize() переписал эту ссылку, и та область данных(объект handle, который создал поток А), которую все еще использует поток В, объявлена как неиспользованая и ее в любой момент может занять любой другой поток.

Я против
Когда поток В колбасится внутри assertSanity(), то this -это просто еще одна ссылка на тот обьект,
на котором был вызван holder.assertSanity(). Если поток С скажет в это время holder=new Holder(43), то это никак не повлияет на this в потоке В и на обьект, на который this указывает.
this это final reference. Поэтому всегда внутри assertSanity() this.n==this.n
А то что вы пишите "объявлена как неиспользованая и ее в любой момент может занять любой другой поток.", так это произойдет только после выхода из assertSanity()
Разве не так?
...
Рейтинг: 0 / 0
Товарищ не понимает
    #34404174
wessen
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
02
А то что вы пишите "объявлена как неиспользованая и ее в любой момент может занять любой другой поток.", так это произойдет только после выхода из assertSanity()
Разве не так?

Думаю, что не так. Т.к. ссылка на объект, которой владеет поток B, будет объявлена недействительной после того, как поток С выйдет из метода initialize(). И соотвтественно объект handler, которым владеет поток В может быть уничтожен сборщиком мусора в любой момент. Именно из-за этого, во время считывания второй n, в ней может оказаться все, что угодно: старое значение, новое значение или мусор, как говорится "как Бог пошлет".
...
Рейтинг: 0 / 0
Товарищ не понимает
    #34404181
Зашедший
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wessenВ том то и дело, что поток С вызвав метод initialize() переписал эту ссылку, и та область данных(объект handle, который создал поток А), которую все еще использует поток В, объявлена как неиспользованая и ее в любой момент может занять любой другой поток.
Это невозможно. До тех пор, пока хоть у одного потока есть ссылка на объект - он не может быть разрушен. Просто потоки будут использовать разные ссылки (они обычно в таком случае кэшируются потоками).
...
Рейтинг: 0 / 0
Товарищ не понимает
    #34404184
Зашедший
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wessenИменно из-за этого, во время считывания второй n, в ней может оказаться все, что угодно: старое значение, новое значение или мусор, как говорится "как Бог пошлет".
Утверждение в корне противоречит спецификации JVM.
...
Рейтинг: 0 / 0
Товарищ не понимает
    #34404221
wessen
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2Зашедший
Если предположить, что поток В не имеет прямой ссылки на класс Handler и вызывает метод assertSanity() через класс описанный в листинге 3.14. Что произойдет, когда кто-либо еще раз вызовет метод initialize()? По сути, не будет существовать прямой ссылки на объект, в котором все еще находится поток В. Собственно вопрос, gc секёт такие случаи? Если да, тогда и я не знаю как может возникнуть ексепшион в даннолм примере.
...
Рейтинг: 0 / 0
Товарищ не понимает
    #34404342
02
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
02
Гость
Кажется товарищ понял
Если поток А, который сказал
holder = new Holder(42);
сбросил в heap ссылку holder на только что созданный Holder(42),
то это еще не значит, что он уже сбросил туда же и окончательное состояние этого нового Holder(42). Единственное, что поток А может гарантировать, что ничего другого в n кроме default value для int т.е. 0 или 42 быть не может, причем в порядке: сначала 0 потом 42, не наоборот
Поэтому из указанных т. Timm вариантов
Timm
Можно!! Варианты:
1. holder == null
2. holder != null && n = 0
3. holder != null && n = 42
4. holder != null && n = <что то еще>
+ состояние объекта при чтении в разные моменты времени может быть разным.

Первые три случая могут быть. Насчет четвертого я не уверен.
поток В, выполняющий assertSanity() может увидеть:
в левом n 0, а в правом 0 или 42,
в левом 42, тогда в правом только 42

Праильно???
...
Рейтинг: 0 / 0
Товарищ не понимает
    #34404360
Зашедший
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
02Праильно???
Ну, я б предположил именно такой поворот событий. Второй поток может использовать закешированное им состояние n=0 несмотря на то, что поток-создаватель уже присвоил ему 42, и "подкачать" новое состояние в процессе определения равенства... умереть объект, на который есть ссылка, не может точно :)
...
Рейтинг: 0 / 0
17 сообщений из 17, страница 1 из 1
Форумы / Java [игнор отключен] [закрыт для гостей] / Товарищ не понимает
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]