2015-12-30 3 views
3

Есть что-то, что я еще не понял о синхронизации и нестабильности.Летучие и синхронизированные

Я понимаю, что поток может безопасно меняться локально. Из того, что я прочитал до сих пор, является синхронизированное> неустойчивое.

Скажем, у меня есть параметр, который не длинный или двойной, поэтому в основном стандартный Integer (без атома).

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

public class stackoverflow { 

    private int x = 0; 

    public synchronized void rechnen(){ 
     //dosomething 
    } 
} 

в основном после rechnen() будет сделан, и я получил 10000 нитей, все будет получить версию обновления х, потому что мой метод синхронизации? или я должен объявить его изменчивым?

+0

Пожалуйста, задавайте вопросы на языке программирования, который вы используете. –

ответ

5

Да, они получат обновленную версию. synchronized гарантирует две вещи: видимость изменений и атомарность. volatile просто гарантирует видимость изменений. Java гарантирует, что код внутри синхронизированного блока не будет оптимизирован (путем смешивания команд внутри и вне блока synchronized), поэтому каждое изменение переменных внутри него будет видимым для всех потоков после окончания синхронизированного блока.

+1

«все изменения в переменных внутри него будут видны для всего потока после окончания синхронизированного блока», это, конечно, правильно, но, я думаю, это неверно. «Java гарантирует, что код внутри синхронизированного блока не будет оптимизирован». –

+0

@ v.ladynev да, вы правы. Я так плохо писал. Я отредактировал ответ. Я думал, что Java не будет оптимизировать код, смешивая заявления внутри и вне блока «synchronized». – partlov

+1

_... каждое изменение переменных внутри него будет видимым для всех потоков после окончания синхронизированного блока. Не совсем верно. JLS только гарантирует, что если поток A обновляет переменную, а затем выходит из синхронизированного блока, то поток B сможет увидеть обновление _after поток B синхронизируется на одном и том же объекте._ –

1

Синхронный метод уже установлен в вашем коде. Кроме того, Вы должны установить ваш х как летучие, как показано ниже

private volatile int x = 0; 

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

Таким образом, вы можете обеспечить ваш код потокобезопасно

+0

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

+0

@RobinGreen Я сказал, что volatile гарантирует, что данные не из кеша, и ему также нужно использовать синхронизированный метод. – Thanga

+0

@Thanga, если все использования одной и той же переменной 'x' синхронизируются на одном и том же объекте, тогда нет необходимости, чтобы' x' была переменной 'volatile'. Спецификация Java Language Spec означает, что все, что хранится в переменных одним потоком до того, как он покинет синхронизированный блок, станет видимым для других потоков после того, как они войдут в любой блок, который синхронизирован на одном и том же объекте. –

3

Да, они получат обновленную версию, но только если они входят в синхронизированный блок самостоятельно. Если они не вводят синхронизированный блок на одном и том же объекте (блокировка одного и того же монитора), то не гарантируется, что они будут видеть обновления.

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

+2

Он не должен быть одним и тем же синхронизированным блоком. Это _does_ должен быть блоком, который синхронизируется с одним и тем же объектом блокировки. –

-1

Объявить приватную переменную int x = 0; это будет служить вашей цели. Для лучшего понимания см. Реализацию AtomicInteger.

2

@partlov уже ответил на ваш вопрос, так как сторона примечания есть некоторые другие вещи, которые вы, возможно, захотите рассмотреть.

Не используйте синхронизируются в качестве модификатора

при объявлении метода, как синхронизируются он использует монитор. В Java каждый Object оказывается монитором, и в этом случае используется экземпляр класса. Таким образом, ваш пример аффективно становится следующим:

public void rechnen(){ 
    synchronized(this) { 
     //dosomething 
    } 
} 

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

Итак, главный совет: всегда держать свои мониторы в тайне.Так что-то вроде:

public class stackoverflow { 
    private final Object monitor = new Object(); 
    private int x = 0; 

    public void rechnen(){ 
     synchronized(monitor) { 
      //dosomething 
     } 
    } 
} 

знать ваше Api

Между volatile и synchronized есть множество инструментов для конкретных целей параллелизма. Большинство из них используют смесь volatile, synchronized и CAS-operations. Например, AtomicInteger дает вам операцию с целым числом атомов с гораздо меньшим количеством конфликтов, как это обычно видно из synchronized. Попробуйте войти по-настоящему знакомы с java.util.concurrent