Upload
vladimir-sitnikov
View
238
Download
1
Tags:
Embed Size (px)
DESCRIPTION
Слово final многогранно и многокрасочно. Ни для кого не секрет, что оно может запрещать наследование классов и запрещать изменение значений полей. Более узкому кругу лиц знакомо, что final обладает особыми свойствами при работе нескольких потоков. К сожалению, в сети много мифов о том какие свойства даёт final, и нет внятного толкования почему и как оно работает. Даже на докладах про java memory model final’ы упоминают лишь вскользь. Что же делать? Совсем не использовать final’ы? Это явно не выбор человека, который любит докапываться до истины! В данном докладе мы расставим все точки в слове final, рассмотрим на конкретных примерах как работает раздел 17.5 спецификации языка Java "Final Field Semantics", а также то, какие бывают типичные ошибки в трактовке спецификации.
Citation preview
Семантика final полей в java
Владимир Ситников, Валентин Коваленко[email protected], @VladimirSitnikv
NetCracker
Сентябрь 2014
Введение
Примеры
2 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Зачем final в JMM?
3 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Безопасность String
String s = ...
if (checkAccess(s)) {
return readFile(s);
}
Правильно ли проверяются права на доступ к файлу?
4 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Без опасность String в java 1.4
String s = ...
if (checkAccess(s)) {
return readFile(s);
}
Ответ зависит от версии, и в java 1.4 возможны проблемы
5 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Без опасность String в java 1.4
String s = GLOBAL;
if (checkAccess(s)) {
return readFile(s);
}
HackThread
GLOBAL =
"/tmp/etc/passwd"
.substring (4);
Например: HackThread выполняет .substring(4) и коварнопередаёт его в поток, читающий файлы
6 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Без опасность String в java 1.4
String s = GLOBAL;
if (checkAccess(s)) {
return readFile(s);
}
HackThread
GLOBAL =
"/tmp/etc/passwd"
.substring (4);
В java 1.4 substring-строка ссылается на тот же массивсимволов, и всё опреледяется полями String#offset и String#size
7 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Без опасность String в java 1.4
String s = GLOBAL;
if (checkAccess(s)) {
return readFile(s);
}
HackThread
GLOBAL =
"/tmp/etc/passwd"
.substring (4);
race
race
Синхронизации между потоками нет, поэтому читатель можетувидеть недоинициализированный объект-строку
8 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Без опасность String в java 1.4
String s = GLOBAL;
if (checkAccess(s)) {
return readFile(s);
}
HackThread
GLOBAL =
"/tmp/etc/passwd"
.substring (4);
race
race
checkAccess может увидеть "/tmp/etc/passwd", а readFile уже"/etc/passwd"
Даже синхронизация на s и volatile не спасут!
9 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Без опасность String в java 1.4
String s = GLOBAL;
if (checkAccess(s)) {
return readFile(s);
}
HackThread
GLOBAL =
"/tmp/etc/passwd"
.substring (4);
race
race
checkAccess может увидеть "/tmp/etc/passwd", а readFile уже"/etc/passwd"Даже синхронизация на s и volatile не спасут!
10 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Безопасность String в java 1.5+
String s = GLOBAL;
if (checkAccess(s)) {
return readFile(s);
}
HackThread
GLOBAL =
"/tmp/etc/passwd"
.substring (4);
hb
hb
В java 1.5+ final защищает от недосозданных объектов иHackTread
11 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Зачем нам JMM?
12 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Код-загадка
int x = 1;
public int neverTryThisAtHome () {
int i = this.x; // it is 1, isn’t it?
this.setX (2); // just updates x to 2
return this.x - i; // 2 - 1 == ...?
}
Какие значения могут вернуться? 1? 0? -1?
13 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Код-загадка
int x = 1;
public int neverTryThisAtHome () {
int i = this.x; // it is 1, isn’t it?
this.setX (2); // just updates x to 2
return this.x - i; // 2 - 1 == ...?
}
Да, тут вернётся 1
14 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Код-загадка
final int x = 1;
public int neverTryThisAtHome () {
int i = this.x; // it is 1, isn’t it?
this.setX (2); // just updates x to 2
return this.x - i; // 2 - 1 == ...?
}
А теперь добавим ножек final
15 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Код-загадка
final int x = 1;
public int neverTryThisAtHome () {
int i = this.x; // it is 1, isn’t it?
this.setX (2); // just updates x to 2
return this.x - i; // 2 - 1 == ...?
}
Спецификация разрешает все варианты: 1, 0, и даже -1! (см.пример 17.5.3-1)
16 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Немного теории
17 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Программный порядок (program order)
I Отражает то, в каком порядке написан исходный код
I Компилятору запрещено переупорядочивать, игнорировать именять операции, если это нарушит program order
I Но это не значит, что всё выполняется именно так, как висходном коде
I Например: для операций над локальными переменнымиprogram order вообще не определён
18 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Программный порядок (program order)
I Отражает то, в каком порядке написан исходный кодI Компилятору запрещено переупорядочивать, игнорировать именять операции, если это нарушит program order
I Но это не значит, что всё выполняется именно так, как висходном коде
I Например: для операций над локальными переменнымиprogram order вообще не определён
19 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Программный порядок (program order)
I Отражает то, в каком порядке написан исходный кодI Компилятору запрещено переупорядочивать, игнорировать именять операции, если это нарушит program order
I Но это не значит, что всё выполняется именно так, как висходном коде
I Например: для операций над локальными переменнымиprogram order вообще не определён
20 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Программный порядок (program order)
I Отражает то, в каком порядке написан исходный кодI Компилятору запрещено переупорядочивать, игнорировать именять операции, если это нарушит program order
I Но это не значит, что всё выполняется именно так, как висходном коде
I Например: для операций над локальными переменнымиprogram order вообще не определён
21 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Частичный порядок (partial order)
I В главе 17 JLS 8 раз упоминается слово "partial order"
I Частичным порядкомhb−−→ является бинарное отношение,
которое:
I Рефлексивно: для любого элемента x выполняется xhb−−→ x
I Антисимметрично: если xhb−−→ y и y
hb−−→ x , то x и y это одно и
то же
I Транзитивно: если xhb−−→ y и y
hb−−→ z , то x
hb−−→ z
22 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Частичный порядок (partial order)
I В главе 17 JLS 8 раз упоминается слово "partial order"
I Частичным порядкомhb−−→ является бинарное отношение,
которое:
I Рефлексивно: для любого элемента x выполняется xhb−−→ x
I Антисимметрично: если xhb−−→ y и y
hb−−→ x , то x и y это одно и
то же
I Транзитивно: если xhb−−→ y и y
hb−−→ z , то x
hb−−→ z
23 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Частичный порядок (partial order)
I В главе 17 JLS 8 раз упоминается слово "partial order"
I Частичным порядкомhb−−→ является бинарное отношение,
которое:
I Рефлексивно: для любого элемента x выполняется xhb−−→ x
I Антисимметрично: если xhb−−→ y и y
hb−−→ x , то x и y это одно и
то же
I Транзитивно: если xhb−−→ y и y
hb−−→ z , то x
hb−−→ z
24 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Частичный порядок (partial order)
I В главе 17 JLS 8 раз упоминается слово "partial order"
I Частичным порядкомhb−−→ является бинарное отношение,
которое:
I Рефлексивно: для любого элемента x выполняется xhb−−→ x
I Антисимметрично: если xhb−−→ y и y
hb−−→ x , то x и y это одно и
то же
I Транзитивно: если xhb−−→ y и y
hb−−→ z , то x
hb−−→ z
25 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Частичный порядок (partial order)
I В главе 17 JLS 8 раз упоминается слово "partial order"
I Частичным порядкомhb−−→ является бинарное отношение,
которое:
I Рефлексивно: для любого элемента x выполняется xhb−−→ x
I Антисимметрично: если xhb−−→ y и y
hb−−→ x , то x и y это одно и
то же
I Транзитивно: если xhb−−→ y и y
hb−−→ z , то x
hb−−→ z
26 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Happens-before
I Основной принцип JMM: рассмотрим все возможныевыполнения и откинем "неправильные"
I Happens-before это частичный порядок, который долженсоблюдаться
I Запрещено видеть "будущие" записи (rhb−−→ w)
I И те, которые были перетёрлись в hb порядке:
w1hb−−→ w2
hb−−→ r
I И те, которые запрещены просто так: 17.4.8 executions andcausality requirements
27 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Happens-before
I Основной принцип JMM: рассмотрим все возможныевыполнения и откинем "неправильные"
I Happens-before это частичный порядок, который долженсоблюдаться
I Запрещено видеть "будущие" записи (rhb−−→ w)
I И те, которые были перетёрлись в hb порядке:
w1hb−−→ w2
hb−−→ r
I И те, которые запрещены просто так: 17.4.8 executions andcausality requirements
28 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Happens-before
I Основной принцип JMM: рассмотрим все возможныевыполнения и откинем "неправильные"
I Happens-before это частичный порядок, который долженсоблюдаться
I Запрещено видеть "будущие" записи (rhb−−→ w)
I И те, которые были перетёрлись в hb порядке:
w1hb−−→ w2
hb−−→ r
I И те, которые запрещены просто так: 17.4.8 executions andcausality requirements
29 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Happens-before
I Основной принцип JMM: рассмотрим все возможныевыполнения и откинем "неправильные"
I Happens-before это частичный порядок, который долженсоблюдаться
I Запрещено видеть "будущие" записи (rhb−−→ w)
I И те, которые были перетёрлись в hb порядке:
w1hb−−→ w2
hb−−→ r
I И те, которые запрещены просто так: 17.4.8 executions andcausality requirements
30 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Happens-before
I Основной принцип JMM: рассмотрим все возможныевыполнения и откинем "неправильные"
I Happens-before это частичный порядок, который долженсоблюдаться
I Запрещено видеть "будущие" записи (rhb−−→ w)
I И те, которые были перетёрлись в hb порядке:
w1hb−−→ w2
hb−−→ r
I И те, которые запрещены просто так: 17.4.8 executions andcausality requirements
31 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Семантика final полей одним слайдом
whb−−→ f
hb−−→ a
mc−−→ r1
dr−→ r2⇒ w
hb∗
−−→ r2
I w и r2 – интересующие нас нас запись и чтение
I f – заморозка final поля, которое читается в r1
32 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Семантика final полей одним слайдом
whb−−→ f
hb−−→ a
mc−−→ r1
dr−→ r2⇒ w
hb∗
−−→ r2
I w и r2 – интересующие нас нас запись и чтениеI f – заморозка final поля, которое читается в r1
33 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Семантика final полей одним слайдом
whb−−→ f
hb−−→ a
mc−−→ r1
dr−→ r2⇒ w
hb∗
−−→ r2
I Если единственный путь от записи к чтению идёт через всеэти стрелочки, то мы не можем увидеть более ранние записи
I Если путей больше одного – как повезёт
34 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Семантика final полей одним слайдом
whb−−→ f
hb−−→ a
mc−−→ r1
dr−→ r2⇒ w
hb∗
−−→ r2
I Если единственный путь от записи к чтению идёт через всеэти стрелочки, то мы не можем увидеть более ранние записи
I Если путей больше одного – как повезёт
35 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Freeze action w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Поле
Freezeслучается в конце конструктора и морозит final поля
36 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Freeze action w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Freeze action
Поле
Freezeслучается в конце конструктора и морозит final поля
37 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Freeze action w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Freeze actionПоле
Freezeслучается в конце конструктора и морозит final поля
38 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Dereference chain: что это? w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T local = GLOBAL; r1
int localX = local.x; r2
I r2 читает поле объекта
I Поток не создавал объектI Значит, где-то мы должны были читать адрес этого объекта
I Это называется r1dr−→ r2
39 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Dereference chain: что это? w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T local = GLOBAL; r1
int localX = local.x; r2
I r2 читает поле объектаI Поток не создавал объект
I Значит, где-то мы должны были читать адрес этого объекта
I Это называется r1dr−→ r2
40 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Dereference chain: что это? w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T local = GLOBAL; r1
int localX = local.x; r2
I r2 читает поле объектаI Поток не создавал объектI Значит, где-то мы должны были читать адрес этого объекта
I Это называется r1dr−→ r2
41 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Dereference chain: что это? w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T local = GLOBAL; r1
int localX = local.x; r2
dr
I r2 читает поле объектаI Поток не создавал объектI Значит, где-то мы должны были читать адрес этого объекта
I Это называется r1dr−→ r2
42 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Dereference chain два потока w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T localA = new T();
GLOBAL = localA;
Thread 2
T localB = GLOBAL; r1
if (localB != null) {
int localX = localB.x; r2
}
dr
r1dr−→ r2 (читаем поле несозданного нами объекта)
43 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Dereference chain два потока w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T localA = new T(); a
GLOBAL = localA;
Thread 2
T localB = GLOBAL; r1
if (localB != null) {
int localX = localB.x; r2
}
dr
dr?
Есть ли adr−→ r2?
44 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Dereference chain два потока w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T localA = new T(); a
GLOBAL = localA;
Thread 2
T localB = GLOBAL; r1
if (localB != null) {
int localX = localB.x; r2
}
dr
dr?
Между потоками dr не возникает!
45 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Dereference chain: контрольный выстрелw hb−→ f hb−→ a mc−→ r1 dr−→ r2
T local = GLOBAL; ra
local = GLOBAL; rb
int localX = local.x; r2
Есть ли здесь dr ?
46 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Dereference chain: контрольный выстрелw hb−→ f hb−→ a mc−→ r1 dr−→ r2
T local = GLOBAL; ra
local = GLOBAL; rb
int localX = local.x; r2
dr?
dr?
Один из radr−→ r2 или rb
dr−→ r2 точно должен быть, но точнее
сказать невозможно47 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Memory chain: что это? w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T o = new T();
GL = o; w1
Thread 2
T o = GL; r1
GL2 = o;
Thread 3
T o = GL2;
int r = o.x;
mc
Если чтение видит запись, то w1mc−−→ r1
48 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Memory chain: что это? w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T o = new T();
GL = o; w1
Thread 2
T o = GL; r1
GL2 = o; w2
Thread 3
T o = GL2;
int r = o.x;
mc
mc
Если пишем адрес созданного врагом объекта, то r1mc−−→ w2
49 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Memory chain: что это? w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T o = new T();
GL = o; w1
Thread 2
T o = GL; r1
GL2 = o; w2
Thread 3
T o = GL2; r3
int r = o.x;
mc
mc
mc
r3 видит w2 ⇒ w2mc−−→ r3
50 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Memory chain: что это? w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T o = new T();
GL = o; w1
Thread 2
T o = GL; r1
GL2 = o; w2
Thread 3
T o = GL2; r3
int r = o.x; r4
mc
mc
mc dr
r3dr−→ r4 (читаем поле объекта)
⇒ r3mc−−→ r4
51 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Memory chain: что это? w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T o = new T();
GL = o; w1
Thread 2
T o = GL; r1
GL2 = o; w2
Thread 3
T o = GL2; r3
int r = o.x; r4
mc
mc
mc dr
mc
r3dr−→ r4 (читаем поле объекта) ⇒ r3
mc−−→ r4
52 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Memory chain: что это? w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T o = new T();
GL = o; w1
Thread 2
T o = GL; r1
GL2 = o; w2
Thread 3
T o = GL2; r3
int r = o.x; r4
mc
mc
mc dr
mc
mc
mc транзитивно (т.к. частичный порядок) ⇒ w1mc−−→ r4
53 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Введение
Примеры
54 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Disclaimer
I Все примеры опасно синхронизированы (иначе зачем мысобрались?)
I Случаи чтения null не рассматриваем (даже если онивозможны)
I При составлении слайдов пострадал не один мозг
55 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Disclaimer
I Все примеры опасно синхронизированы (иначе зачем мысобрались?)
I Случаи чтения null не рассматриваем (даже если онивозможны)
I При составлении слайдов пострадал не один мозг
56 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Disclaimer
I Все примеры опасно синхронизированы (иначе зачем мысобрались?)
I Случаи чтения null не рассматриваем (даже если онивозможны)
I При составлении слайдов пострадал не один мозг
57 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Эталонный final (1) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
}};
GLOBAL = l;
Thread 2
T o = GLOBAL;
if (o != null) {
int result = o.fx; r2
}
Возможно ли в result получить 0?
58 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Эталонный final (1) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
}}; f
GLOBAL = l; a
Thread 2
T o = GLOBAL;
if (o != null) {
int result = o.fx;
}
hb
hb
Действия в одном потоке образуют happens-before:
whb−−→ f , f
hb−−→ a
59 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Эталонный final (1) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
}}; f
GLOBAL = l; a
Thread 2
T o = GLOBAL; r0
if (o != null) {
int result = o.fx;
}
hb
hb
mc
r0 видит запись a :
amc−−→ r0
60 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Эталонный final (1) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
}}; f
GLOBAL = l; a
Thread 2
T o = GLOBAL; r0
if (o != null) {
int result = o.fx; r1
}
hb
hb
mc
dr
Поток 2 не создавал объект, r1 читает его поле, а r этоединственное чтение адреса объекта, поэтому dereference chain:
r0dr−→ r1
61 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Эталонный final (1) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
}}; f
GLOBAL = l; a
Thread 2
T o = GLOBAL; r0
if (o != null) {
int result = o.fx; r1
}
hb
hb
mc
dr
mc
r0dr−→ r1⇒ r0
mc−−→ r1
62 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Эталонный final (1) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
}}; f
GLOBAL = l; a
Thread 2
T o = GLOBAL; r0
if (o != null) {
int result = o.fx; r1
}
hb
hb
mc
mc
mc
amc−−→ r1 ( mc транзитивно)
63 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Эталонный final (1) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
}}; f
GLOBAL = l; a
Thread 2
T o = GLOBAL;
if (o != null) {
int result = o.fx; r1 r2
}
hb
hb
mc
dr
Возьмём r2 = r1, тогда r1dr−→ r2 ( dr рефлексивно)
64 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Эталонный final (1) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
}}; f
GLOBAL = l; a
Thread 2
T o = GLOBAL;
if (o != null) {
int result = o.fx; r1 r2
}
hb
hb
mc
dr
hb*
Нашли всё необходимое для HB∗:
whb−−→ f
hb−−→ a
mc−−→ r1
dr−→ r2⇒ w
hb∗
−−→ r2
65 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Эталонный final (1) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
}}; f
GLOBAL = l; a
Thread 2
T o = GLOBAL;
if (o != null) {
int result = o.fx; r1 r2
}
hb
hb
mc
dr
hb*
(whb∗
−−→ r2)⇒ result ∈ {42}
66 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Массив внутри final (2) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T l = new T() {{
int[] u = new int [1];
u[0] = 42; w
fx = u;
}};
GLOBAL = l;
T o = GLOBAL;
if (o != null) {
int[] lfx = o.fx;
int result = lfx [0]; r2
}
Возможно ли в result получить 0?
67 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Массив внутри final (2) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T l = new T() {{
int[] u = new int [1];
u[0] = 42; w
fx = u;
}}; f
GLOBAL = l; a
T o = GLOBAL;
if (o != null) {
int[] lfx = o.fx;
int result = lfx [0]; r2
}
hb
hb
Действия в одном потоке образуют happens-before:
whb−−→ f , f
hb−−→ a
68 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Массив внутри final (2) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T l = new T() {{
int[] u = new int [1];
u[0] = 42; w
fx = u;
}}; f
GLOBAL = l; a
T o = GLOBAL;
if (o != null) {
int[] lfx = o.fx; r1
int result = lfx [0]; r2
}
hb
hb
mc
r1 видит запись a (т.к. простое final поле):
amc−−→ r1
69 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Массив внутри final (2) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T l = new T() {{
int[] u = new int [1];
u[0] = 42; w
fx = u;
}}; f
GLOBAL = l; a
T o = GLOBAL;
if (o != null) {
int[] lfx = o.fx; r1
int result = lfx [0]; r2
}
hb
hb
mc
dr
Поток 2 не создавал массив, r2 читает его элемент, r1 этоединственное чтение адреса массива, поэтому dereference chain:
r1dr−→ r2
70 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Массив внутри final (2) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T l = new T() {{
int[] u = new int [1];
u[0] = 42; w
fx = u;
}}; f
GLOBAL = l; a
T o = GLOBAL;
if (o != null) {
int[] lfx = o.fx; r1
int result = lfx [0]; r2
}
hb
hb
mc
dr
hb*
Нашли всё необходимое для HB∗:
whb−−→ f
hb−−→ a
mc−−→ r1
dr−→ r2⇒ w
hb∗
−−→ r2
71 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Массив внутри final (2) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T l = new T() {{
int[] u = new int [1];
u[0] = 42; w
fx = u;
}}; f
GLOBAL = l; a
T o = GLOBAL;
if (o != null) {
int[] lfx = o.fx; r1
int result = lfx [0]; r2
}
hb
hb
mc
dr
hb*
(whb∗
−−→ r2)⇒ result ∈ {42}
72 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Массив наоборот (2.1) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T l = new T() {{
int[] u = new int [1];
fx = u; // (!)
u[0] = 42; w
}};
GLOBAL = l;
T o = GLOBAL;
if (o != null) {
int[] lfx = o.fx;
int result = lfx [0];
}
Возможно ли в result получить 0?
73 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Массив наоборот (2.1) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T l = new T() {{
int[] u = new int [1];
fx = u; // (!)
u[0] = 42; w
}}; f
GLOBAL = l; a
T o = GLOBAL;
if (o != null) {
int[] lfx = o.fx; r1
int result = lfx [0]; r2
}
hbhb
mc
dr
hb∗
Строим hb∗ так же как и в предыдущем случае
74 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Массив наоборот (2.1) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T l = new T() {{
int[] u = new int [1];
fx = u; // (!)
u[0] = 42; w
}}; f
GLOBAL = l; a
T o = GLOBAL;
if (o != null) {
int[] lfx = o.fx; r1
int result = lfx [0]; r2
}
hbhb
mc
dr
hb∗
(whb∗
−−→ r2)⇒ result ∈ {42}Результат не зависит от порядка записи final полей!
75 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Утекание this (3) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
GLOBAL = this;
}};
Thread 2
T o = GLOBAL;
if (o != null) {
int result = o.fx; r2
}
Возможно ли в result получить 0?
76 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Утекание this (3) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
GLOBAL = this; a
}}; f
Thread 2
T o = GLOBAL;
if (o != null) {
int result = o.fx; r2
}
hb
hb
Действия в одном потоке образуют happens-before:
whb−−→ f , a
hb−−→ f
77 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Утекание this (3) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
GLOBAL = this; a
}}; f
Thread 2
T o = GLOBAL;
if (o != null) {
int result = o.fx; r2
}
hb
hb
hb?
Но нам-то нужно fhb−−→ a!
78 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Утекание this (3) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
GLOBAL = this; a
}}; f
Thread 2
T o = GLOBAL;
if (o != null) {
int result = o.fx; r2
}
hb
hb
hb?
Если ahb−−→ f и f
hb−−→ a, то a = f (антисимметричность hb )
Но публикация ссылки это никак не freeze action!
79 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Утекание this (3) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
GLOBAL = this; a
}}; f
Thread 2
T o = GLOBAL;
if (o != null) {
int result = o.fx; r2
}
hb
hb
hb?
Нет hb∗ , поэтому возможны все варианты: result ∈ {0, 42}
80 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Вредительское утекание this (4) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
GLb = this;
}};
GLa = l;
Thread 2
T u = GLb;
T o = GLa;
if (o != null) {
int result = o.fx; r2
}
Возможно ли в result получить 0?
81 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Вредительское утекание this (4) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
GLb = this; b
}}; f
GLa = l; a
Thread 2
T u = GLb;
T o = GLa;
if (o != null) {
int result = o.fx;
}
hb
hb
hb?
Действия в одном потоке образуют happens-before:
whb−−→ f , f
hb−−→ a
82 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Вредительское утекание this (4) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
GLb = this; b
}}; f
GLa = l; a
Thread 2
T u = GLb;
T o = GLa; ra
if (o != null) {
int result = o.fx;
}
hb
hb
hb?
mc
Пусть второй поток увидел GLa, тогда amc−−→ ra!
83 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Вредительское утекание this (4) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
GLb = this; b
}}; f
GLa = l; a
Thread 2
T u = GLb; rb
T o = GLa; ra
if (o != null) {
int result = o.fx; r1
}
hb
hb
hb?
mc
dr?
dr?
Где-то должно быть dr : rbdr−→ r1 или ra
dr−→ r1
84 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Вредительское утекание this (4) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
GLb = this; b
}}; f
GLa = l; a
Thread 2
T u = GLb; rb
T o = GLa; ra
if (o != null) {
int result = o.fx; r1
}
hb
hb
hb?
mc
dr?
dr
Если rbdr−→ r1, то мы попали, т.к. w
hb∗
−−→ r1 не строится
85 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Вредительское утекание this (4) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
T l = new T() {{
fx = 42; w
GLb = this; b
}}; f
GLa = l; a
Thread 2
T u = GLb; rb
T o = GLa; ra
if (o != null) {
int result = o.fx; r1
}
hb
hb
hb?
mc
dr?
dr
Вывод: если наш поток уже читал объект с незамороженнымиполями, то гарантий final semantics нет!
86 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Reflection in action (5) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T t = new T() {{
fu = new U();
fu.x = 1; w1
}};
GLOBAL = t;
U w = new U();
w.x = 42; w2
reflectSet(t.fu , w);
Thread 2
T t = GLOBAL;
if (t != null) {
U u = t.fu;
int result = u.x; r2
}
Возможно ли в result получить 0?
87 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Reflection in action (5) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T t = new T() {{
fu = new U();
fu.x = 1;
}};
GLOBAL = t;
U w = new U();
w.x = 42; w2
reflectSet(t.fu , w);
Thread 2
T t = GLOBAL;
if (t != null) {
U u = t.fu;
int result = u.x; r2
}
hb*?
По-хорошему, нам нужно w2hb∗
−−→ r2 (иначе не запрещено видетьзапись 0 в w.x)
88 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Reflection in action (5) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T t = new T() {{
fu = new U();
fu.x = 1;
}};
GLOBAL = t;
U w = new U();
w.x = 42; w2
reflectSet(t.fu , w); f2
Thread 2
T t = GLOBAL;
if (t != null) {
U u = t.fu;
int result = u.x; r2
}
hb*?
f2 не подходит для hb∗ : нет подходящего f 2hb−−→ a
89 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Reflection in action (5) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T t = new T() {{
fu = new U();
fu.x = 1;
}}; f1
GLOBAL = t;
U w = new U();
w.x = 42; w2
reflectSet(t.fu , w); f2
Thread 2
T t = GLOBAL;
if (t != null) {
U u = t.fu;
int result = u.x; r2
}
hb*?
hb?
f1 тоже не подходит для hb∗ : должно быть w2hb−−→ f 1
90 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Reflection in action (5) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T t = new T() {{
fu = new U();
fu.x = 1;
}}; f1
GLOBAL = t;
U w = new U();
w.x = 42; w2
reflectSet(t.fu , w); f2
Thread 2
T t = GLOBAL;
if (t != null) {
U u = t.fu;
int result = u.x; r2
}
hb*?
hb?
Получается, что чтению r2 не запрещено видеть 0
91 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Reflection in action (5) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T t = new T() {{
fu = new U();
fu.x = 1;
}}; f1
GLOBAL = t; a
U w = new U();
w.x = 42; w2
reflectSet(t.fu , w); f2
Thread 2
T t = GLOBAL;
if (t != null) {
U u = t.fu;
int result = u.x; r2
}
hb*?
hb?
Итого: после публикации менять final поля опасноresult ∈ {0, 1, 42}
92 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Чиним reflection (5.1) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
T t = new T() {{
fu = new U();
fu.x = 1;
}};
U w = new U();
w.x = 42;
reflectSet(t.fu , w);
GLOBAL = t; a
Thread 2
T t = GLOBAL;
if (t != null) {
U u = t.fu;
int result = u.x;
}
Если публиковать ссылку после всех изменений final полей, товсё работает: result ∈ {1, 42}
93 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Final wrapper (6) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
A a = new O() {{
fx = 42; w
}};
GL = a;
Thread 2
A a = GL;
B = new B() {{
fb = o;
}};
GL2 = b;
Thread 3
B B = GL2;
A a = b.fb;
int r = a.fx; r2
Возможно ли в result получить 0?
94 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Final wrapper (6) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
A a = new O() {{
fx = 42; w
}};
GL = a; wa
Thread 2
A a = GL; ra
B = new B() {{
fb = o; wb
}};
GL2 = b;
Thread 3
B B = GL2;
A a = b.fb; rb
int r = a.fx; r1
mc
mc
rb видит wb ⇒ wbmc−−→ rb
ra видит запись wa : wamc−−→ ra
95 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Final wrapper (6) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
A a = new O() {{
fx = 42; w
}};
GL = a; wa
Thread 2
A a = GL; ra
B = new B() {{
fb = o; wb
}};
GL2 = b;
Thread 3
B B = GL2;
A a = b.fb; rb
int r = a.fx; r1
mc
mc
mc
Поток 2 пишет адрес созданного врагом объекта, поэтому
ramc−−→ wb
96 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Final wrapper (6) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
A a = new O() {{
fx = 42; w
}};
GL = a; wa
Thread 2
A a = GL; ra
B = new B() {{
fb = o; wb
}};
GL2 = b;
Thread 3
B B = GL2;
A a = b.fb; rb
int r = a.fx; r1
mc
mc
mcdr
Поток 3 не создавал объект A, r читает его поле, а rb этоединственное чтение адреса объекта, поэтому dereference chain:
rbdr−→ r1
97 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Final wrapper (6) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
A a = new O() {{
fx = 42; w
}};
GL = a; wa
Thread 2
A a = GL; ra
B = new B() {{
fb = o; wb
}};
GL2 = b;
Thread 3
B B = GL2;
A a = b.fb; rb
int r = a.fx; r1
mc
mc
mcdr
mc
rbdr−→ r1⇒ rb
mc−−→ r1
98 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Final wrapper (6) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
A a = new O() {{
fx = 42; w
}};
GL = a; wa
Thread 2
A a = GL; ra
B = new B() {{
fb = o; wb
}};
GL2 = b;
Thread 3
B B = GL2;
A a = b.fb; rb
int r = a.fx; r1
mc
mc
mc
mc
mc
mc транзитивно (т.к. частичный порядок) ⇒ wamc−−→ r1
99 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Final wrapper (6) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
A a = new O() {{
fx = 42; w
}}; f
GL = a; wa
Thread 2
A a = GL; ra
B = new B() {{
fb = o; wb
}};
GL2 = b;
Thread 3
B B = GL2;
A a = b.fb; rb
int r = a.fx; r1 r2
mc
dr
Возьмём r2 = r1, тогда r1dr−→ r2 ( dr рефлексивно)
100 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Final wrapper (6) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
A a = new O() {{
fx = 42; w
}}; f
GL = a; wa
Thread 2
A a = GL; ra
B = new B() {{
fb = o; wb
}};
GL2 = b;
Thread 3
B B = GL2;
A a = b.fb; rb
int r = a.fx; r1 r2
mc
dr
hb
hb
hb*
Нашли всё необходимое для HB∗:
whb−−→ f
hb−−→ a
mc−−→ r1
dr−→ r2⇒ w
hb∗
−−→ r2101 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Final wrapper (6) w hb−→ f hb−→ a mc−→ r1 dr−→ r2
Thread 1
A a = new O() {{
fx = 42; w
}}; f
GL = a; wa
Thread 2
A a = GL; ra
B = new B() {{
fb = o; wb
}};
GL2 = b;
Thread 3
B B = GL2;
A a = b.fb; rb
int r = a.fx; r1 r2
mc
dr
hb
hb
hb*
(whb∗
−−→ r2)⇒ result ∈ {42}
102 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved
Вопросы?
103 / 103 (c) Copyright 2014, NetCracker Technology Corp. All rights reserved