Java синхронизируется и происходит раньше

Синхронизированный оператор устанавливает отношение "происходит до". Но я не уверен в деталях. В http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html можно прочитать

Разблокировка (синхронизированный блок или выход метода) монитора происходит перед каждой последующей блокировкой (синхронизированный блок или вход метода) того же монитора

Я хочу знать, правильно ли я это понял. Поэтому взгляните на следующий пример. Предположим, что есть 2 потока T1,T2, совместно использующих одни и те же данные экземпляра класса Data и объекта класса Object. Теперь следующий код выполняется в заданных потоках и порядке:

(1)T1:  data.setValue("newValue");
(2)T1:  synchronized(object){}

(3)T2:  synchronized(object){}
(4)T2:  String str=data.getValue();

Поскольку (1) и (2) выполняются в одном потоке, один имеет hb(1,2) и аналог hb(3,4). В (2) разблокировка монитора, а в (3) блокировка того же монитора, таким образом, hb(2,3), поэтому hb(1,4) и str должны быть равны "newValue". Это верно? Если нет, то hb (2,3) должно быть не так, но почему?

редактировать

Поскольку детали класса Data необходимы для ответа на вопрос:

public class Data {

private String value

 public void setValue(String newValue){
     value=newValue;
 }

 public String getValue getValue(){
     return value;
 }
}

Редактировать 2 ясно, что никто не может гарантировать порядок исполнения. Когда вместо

(1*)T1:  synchronized(object){data.setValue("newValue");}

(2*)T2:  synchronized(object){String str=data.getValue();}

у человека также нет гарантии, что (1*) будет выполнено до (2*), но если я прав, у него есть гарантия, что после (2 *) у него есть str= "newValue", если (1*) было выполнено до (2) *). Я хочу знать, верно ли то же самое для 1-го примера

3 ответа

Решение

Поскольку (1) и (2) выполняются в одном потоке, один имеет hb(1,2) и аналог hb(3,4). В (2) разблокировка монитора, а в (3) блокировка того же монитора, таким образом, hb(2,3), поэтому hb(1,4) и str должны быть равны "newValue". Это верно?

Да, ваша логика верна для этого конкретного сценария. Если и только если) 2 выполняется раньше 3 затем hb(2, 3), Чтобы понять, почему это так, представьте себе процесс потока, такой как следующий:

localState *= 2;
synchronized(object) {
    sharedState = localState;
}

Хотя localState вычисляется вне синхронизированного блока, другие потоки должны видеть это вычисление, чтобы также видеть правильное значение для sharedState,

Однако важно понимать, что нет никаких оснований ожидать, что заказ, о котором вы просили, является результатом. Например, это может так же легко произойти следующим образом:

(1) T1: data.setValue ("newValue");

(3) T2: синхронизирован (объект){}
(4)T2:  String str=data.getValue();

(2)T1: синхронизированный (объект) {}

Это плохо, потому что сейчас T1 пишет в ячейку памяти без синхронизации T2 собирается прочитать это. (T2 мог даже читать одновременно запись происходит!)


Чтобы понять, что происходит раньше, нужно представить себе, что эти потоки работают одновременно (как это делают потоки) и выполняются в следующем графике:

  | T1 | T2
-------------------------------------------------- -----------
1 | синхронизированный (объект){}     |
2 | data.setValue("новое_значение"); | String str=data.getValue();
3 |                            | синхронизированы (объект) {}

Обратите внимание, как я выровнял эти гипотетические действия.

  • В точке 1, T1 получает замок и отпускает его.
  • В точке 2, T1 выполняет запись в то же время T2 выполняет чтение
  • В точке 3, T2 получает замок и отпускает его.

Но что на самом деле происходит первым в точке 2? T1напиши или T2прочитал?

Синхронизация не гарантирует порядок выполнения потоков по отношению друг к другу. Вместо этого речь идет о согласованности памяти между потоками.

В точке 2потому что нет синхронизации, даже если T1 на самом деле делает запись раньше T2 читает это, T2 свободно видеть старое значение в памяти. Поэтому может показаться, что T2(2) случилось раньше T1(2),

Технически это означает, что вне синхронизации поток свободен для чтения / записи в кэш-памяти ЦП вместо основной памяти. Синхронизация заставляет чтение / запись в основной памяти.

Теперь со второй параллельной шкалой времени:

             T1 | T2
-------------------------------------------------- ----------
 синхронизированный (объект){       | синхронизированы (объект){
  data.setValue("новое_значение"); |  String str=data.getValue();
 }                           | }

Хотя у нас нет гарантии того, какой поток сначала получит блокировку, у нас есть гарантия того, что доступ к памяти будет согласованным. У нас также есть гарантия того, что их действия не будут пересекаться, что было возможно в первые сроки.

  • Если T1 сначала получает блокировку, гарантируется, что T1Синхронизированные действия будут выглядеть так, как будто они происходят раньше T2действия. (T1 обязательно напишу раньше T2 читает.)
  • Если T2 сначала получает блокировку, гарантируется, что T2Синхронизированные действия будут выглядеть так, как будто они происходят раньше T1действия. (T1 обязательно напишу после T2 читает.)

Нет. Нет необходимости, чтобы оператор 2 всегда выполнялся или происходил до оператора 3. Может случиться так, что поток 2 получит монитор объекта, и, следовательно, оператор 3 произойдет до оператора 2.

У вас нет контроля над тем, какой поток фактически получит монитор объекта, и вы не можете предсказать.

Это не так просто. Это также зависит от того, что data.setValue а также data.getValue на самом деле делать под одеялом. Безопасны ли эти методы для одновременных (несинхронизированных) вызовов? В одном надуманном примере, если данные были подкреплены HashMap и несколько потоков одновременно вызывают различные методы множества, это может привести к бесконечному циклу.

Короче говоря, вы можете только гарантировать порядок исполнения. У вас есть некоторые ограниченные гарантии видимости памяти между set и get, но не одновременные вызовы set или get с потенциальными побочными эффектами.

Другие вопросы по тегам