2016-08-20 6 views
5

Я читаю «Общие сведения о расширенных возможностях и передовой практике JVM», в которых есть сегмент кода, который объясняет, - до правила в java. Я не могу понять. Код ниже:если поток A начинается до потока B в java, тогда A будет назначаться os до B?

private int value = 0; 
//executed by Thread A 
public void setValue(int value){ 
    this.value = value; 
} 
//executed by Thread B 
public void getValue(){ 
    return value; 
} 

Предположим, что нить A начинается до резьбы B в коде. Я могу понять, что мы не знаем результат, возвращенный getValue() в Thread B, потому что он не является потокобезопасным. Но в книге говорится, что если добавить синхронизированное ключевое слово для функции setValue() и getValue(), тогда нет проблемы с потоком, и метод getValue() вернет правильное значение. В книге объясняется, что, поскольку synchronized встречается с правилом «бывает раньше». Таким образом, у меня есть два вопроса ниже кода.

public class VolatileDemo3 { 
    private volatile int value = 0; 
    public static void main(String[] args) { 
     VolatileDemo3 v = new VolatileDemo3(); 
     Thread A = new Thread(v.new Test1());// Thread A 
     Thread B = new Thread(v.new Test2());//Thread B 
     A.start(); 
     B.start(); 
    } 
    public void setValue(int value){ 
     this.value = value; 
    } 
    public int getValue(){ 
     return this.value; 
    } 

    public class Test1 implements Runnable { 
     @Override 
     public void run() { 
      setValue(10); 
     } 
    } 
    public class Test2 implements Runnable { 
     @Override 
     public void run() { 
      int v = getValue(); 
      System.out.println(v); 
     } 
    } 
} 
  1. Хотя A.start() пробег перед тем B.start() и значением является volatile, мы не можем обеспечить поток B может распечатать 10, верно? Поскольку нить B возможно планируется сначала JVM, то поток B напечатает 0 не 10.
  2. Даже если нить A планируется провести до резьбы B на JVM, но мы также не можем гарантировать, что команда this.value = value выполняется JVM, прежде чем return this.value потому что JVM снова сортирует инструкции. Правильно ли я понимаю? Пожалуйста, помогите мне.
+3

Существует различие между «безопасным потоком» и «предсказуемым». Добавление 'synchronized' будет означать, что Thread B всегда будет видеть _correct_' значение', но он, возможно, еще не был обновлен. Как вы заметили, нет гарантии, что два потока будут выполняться в предполагаемом порядке. Без 'synchronized' не существует _visibility warranty_, поэтому даже если A выполняется первым, B все еще может видеть старое значение. –

+0

Я думаю, что вы неправильно читаете. Если есть ключевое слово «synchronized», это означает, что JVM гарантирует, что в любой данный момент вы не сможете одновременно выполнять два потока, выполняющих одновременно «setValue» и «getValue» (каждый из них должен завершить выполнение своего метода до другой поток может ввести другой метод). Он не * ничего не гарантирует о порядке этих операций. – Paolo

+1

Чтобы обеспечить «правильную» видимость, вы должны объявить 'private volatile int value = 0;'. Это заставляет задуматься, что написанное значение увидено всеми остальными потоками после. – PeterMmm

ответ

9

Вопрос о «произошло раньше» не то, что он вызывает нить А, чтобы установить значение до потока В. Это то, что, хотя это может случиться так, что поток А получил в this.value = value хронологически перед тем поток B должен бежать getValue , значение B видит все еще прежнее значение.

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

Если поток B сначала вызвал метод, он всегда будет иметь старое значение. Но если это вызвало второй метод, неизвестно, получит ли оно старое или новое значение.

По этой причине вы должны использовать средства для обеспечения правильности «произойти до», а затем вы знаете, что результаты того, что «произошло раньше», видны тем, что происходит «после».

Так, если value является летучим, это гарантирует, что если setValue() вызывается потоком A перед потоком B, тогда поток B увидит новое значение.

 
╔═════════════════════╤════════════════════════╤═════════════════════╗ 
║ Order of operations │ Are we using   │ What value of value ║ 
║      │ volatile/synchronized? │ will B see?   ║ 
╠═════════════════════╪════════════════════════╪═════════════════════╣ 
║ A runs setValue(10) │ N      │ Unknown    ║ 
║ B runs getValue() ├────────────────────────┼─────────────────────╢ 
║      │ Y      │ 10     ║ 
╟─────────────────────┼────────────────────────┼─────────────────────╢ 
║ B runs getValue() │ N      │ 0     ║ 
║ A runs setValue(10) ├────────────────────────┼─────────────────────╢ 
║      │ Y      │ 0     ║ 
╚═════════════════════╧════════════════════════╧═════════════════════╝ 

Что касается ваших двух вопросов:

  1. True. Вы не можете знать, кто из них добирается до этой инструкции. Это не только вопрос о том, какой поток запланирован в первую очередь. Потоки могут работать на разных процессорах, одному процессору может потребоваться длинная выборка памяти, а другая - только короткая выборка памяти, поэтому она медленнее, чем другая. Кроме того, может случиться, что машинные инструкции для подготовки кода имеют разную длину. В общем, вы просто не знаете, что происходит за кулисами, а Java не дает никаких гарантий относительно порядка выполнения потоков.
  2. Маловероятно, что инструкции будут переупорядочены в этом конкретном случае, потому что методы очень короткие.Опять же, вы не можете сказать, что происходит, потому что это зависит от конкретной JVM, количества процессоров, типа процессора, планировщика и памяти - у вас нет гарантий.
+0

Это настоящая проблема, все остальные полностью не имеют смысла. –

+0

Матрица условий и результатов действительно классная. –

+0

Большое спасибо @RealSkeptic. Но я должен убедиться в другом вопросе. Видно с кодом – sunny

0

Добавление синхронизируется с функциями setValue/getValue означает, что любой поток, который хочет, чтобы выполнить этот кусок кода сначала придется получить (или ждать) для блокировки на этом объекте.

Если мы предположим, что блокировки не удерживаются до того, как Thread A вызовет setValue/getValue, Thread A немедленно получит блокировку. Однако в промежуточный период, если поток B вызывает setValue/getValue, ему придется подождать, пока Thread A откажется от блокировки, прежде чем сможет выполнить этот метод.

Однако, если бы оба потока ожидали блокировки объекта, мы не можем гарантировать, какой из них был бы первым выбран ОС.

Смежные вопросы