2014-11-28 1 views
23

Если несколько потоков пытаются обновить одну и ту же переменную-член, это называется условием гонки. Но меня больше интересовало, как JVM обрабатывает его внутренне, если мы не будем обрабатывать его в нашем коде, сделав его синхронизированным или что-то еще? Будет ли это зависать моя программа? Как будет реагировать JVM на это? Я думал, что JVM временно создаст блок синхронизации для этой ситуации, но я не уверен, что именно произойдет.Как JVM внутренне управляет условиями гонки?

Если у вас есть какое-то понимание, было бы хорошо знать.

+4

Это удивительный вопрос, потому что Java является одним из немногих «интерпретируемых» языков с виртуальной машиной, которая имеет реальную поддержку потоковой передачи. Потоки Java действительно выполняются с собственными потоками, где такие языки, как Python, имеют поддельную поддержку потоков (я смотрю на вас, GIL). – vz0

+0

Я нашел какое-то обсуждение [здесь] (http://programmers.stackexchange.com/questions/262428/race-conditions-in-jvm-languages-versus-cc), ожидая, чтобы увидеть, что SO должно сказать в глубине – mprabhat

+0

it не делает. какая нить работает в первую очередь (что зависит от множества параметров, в том числе от внешних) выигрывает гонка. Результат обычно не предсказуем. Сама JVM даже не знает, что существует гонка данных. – njzk2

ответ

15

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

Единственный способ получить реальное изображение - перейти к изучению главы модели памяти в Спецификации языка Java, но это упрощенное представление: всякий раз, когда у вас есть гонка данных, почти нет гарантии относительно результата и поток чтения может видеть любое значение, которое когда-либо было записано в переменную. В этом и заключается единственная гарантия: нить будет не наблюдать за значением «вне тонкого воздуха», которое никогда не было написано. Хорошо, если вы не имеете дело с long s или double s, тогда вы можете увидеть порванные записи.

+0

Я попытался найти точное anwser Marko в спецификации java lang, но не смог достичь точного ответа. Результат не является гарантированным, но все, что я не хочу, - это недружелюбное поведение, такое как система не отвечает, поскольку ни один из потоков не получает доступ к обновлению! – krmanish007

+2

Вы говорите о взаимоблокировках? Без блокировки (например, «синхронизированные» блоки) они не могут возникнуть. –

+0

Определенно это не будет мертвым замком, поскольку нет цикла ожидания, но его просто JVM не знает, что делать, поскольку JVM имеет два запроса одновременно! Я думаю, что нужно подождать, пока другой закончит, прежде чем разрешить другим потокам обновляться. – krmanish007

5

Возможно, мне что-то не хватает, но что с этим нужно обращаться? По-прежнему есть поток, который попадет туда первым. В зависимости от того, какой поток есть, этот поток будет просто обновлять/читать некоторую переменную и перейти к следующей инструкции. Он не может магически построить блок синхронизации, он действительно не знает, что вы хотите сделать. То есть, другими словами, то, что произойдет, будет зависеть от результата «расы».

Примечание. Я не сильно разбираюсь в материалах нижнего уровня, поэтому, возможно, я не полностью понимаю глубину вашего вопроса.

+0

Если существует некоторая разница во времени, то это хорошо, но если JVM уже находится в процессе обновления объекта-члена, и до того, как он закончит, другой поток пытается обновить его снова. Это может произойти в текущей многопоточной модели. В идеале JVM должен дождаться завершения первого, но затем это блок синхронизации temp! – krmanish007

+0

'в процессе обновления объекта-члена'. В этом весь смысл. Некоторые операции являются атомарными ('i = 1'), все остальные могут быть нарушены другим потоком, но JVM не знает, что это не тот эффект, который ваш код. (также, это то, что «синхронизировано» для) – njzk2

+0

Спасибо, с нетерпением жду других ответов на этот вопрос, но мне нравится ссылка mprabhat в комментариях исходного вопроса. –

2

Java предоставляет synchronized и volatile для решения этих ситуаций. Использование их должным образом может быть довольно сложно, но имейте в виду, что Java только раскрывает сложность современных архитектур процессора и памяти. Альтернативами было бы всегда ошибаться на стороне осторожности, эффективно синхронизировать все, что могло бы убить производительность; или игнорировать проблему и не предлагать никакой безопасности потока. И, к счастью, Java обеспечивает отличные конструкторы высокого уровня в пакете java.util.concurrent, поэтому вы часто можете избегать работы с низкоуровневыми материалами.

+0

Я не думаю, что это действительно ответ на вопрос. Речь идет об эффекте, когда вы НЕ используете сами понятия синхронизации. –

+0

Да, Кевину, всегда полезно использовать concurrentHashMap или что-то подобное, чтобы избежать такой ситуации, которая является наиболее оптимальным решением в этой ситуации, но меня больше интересовало, что произойдет, если мы используем базовый API-интерфейс коллекции! – krmanish007

0

JVM прекрасно справится с ситуацией (то есть не будет висеть или жаловаться), но вы не можете получить результат, который вам нравится!

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

public class IntCounter { 
    private int i; 

    public IntCounter(int i){ 
     this.i = i; 
    } 

    public void incrementInt(){ 
     i++; 
    } 

    public int getInt(){ 
     return i; 
    } 
} 

имеет недостатки во многих отношениях.

Во-первых, предположим, что i в настоящее время 0 и нить A и нить B оба вызова incrementInt() примерно в то же время. Существует опасность того, что они оба увидят, что i равно 0, затем оба увеличивают его 1, а затем сохраняют результат. Поэтому в конце двух вызовов я только 1, а не 2!

Это проблема состояния гонки с кодом, но есть и другие проблемы, касающиеся видимости памяти.Когда поток A изменяет общую переменную, нет гарантии (без синхронизации), что нить B когда-либо увидит изменения!

Таким образом, поток A может увеличивать i 100 раз, а через час поток B, вызывая getInt(), может видеть i как 0 или 100 или где-нибудь посередине!

Единственная нормальная вещь, которую нужно сделать, если вы вникаете в java-параллелизм, - это прочитать параллельность Java на практике Брайана Гетца и др. (OK есть, вероятно, другие хорошие способы, чтобы узнать об этом, но это большая книге со написано Джошуа Блох, Дуг Леа и другие)

+1

О, Боже мой, ты вокруг 10-го человека даже не понял этого вопроса ... – peterh

+0

Вопрос П. был: «Будет ли это винить мою программу, как JVM отреагирует на это?» Я ответил, что «он не будет висеть или жаловаться», который, как мне кажется, удовлетворительно отвечает на его вопрос. Поскольку его вопрос предполагал, что он не знаком с java-параллелизмом в целом, я дал ему дополнительную информацию, которая, как я думал, может помочь ему избежать вопиющих ошибок. Мне нравится пытаться быть конструктивным. Меня интересует, почему вы думаете, что я не понял его вопроса или что вы думаете о его вопросе. – user384842

+0

@ krmanish007 Нет JVM. Существует только спецификация языка Java (JLS). _A_ JVM (не _JVM) является одним из компонентов во время выполнения большинства реализаций JLS. Если какой-либо конкретный JVM не сможет выполнить то, что говорит JLS, то это JVM, это неправильно. JLS - это авторитет. JLS не просто читать, но он говорит, что может случиться, а что может не произойти, когда два разных потока обращаются к одной и той же переменной без синхронизации. –

0

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

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

В частности, спецификации языка Java guarantees следующее только в отсутствии каких-либо расы данных:

  • видимости: чтение поля дает значение последнем возложенное на него (не ясно, какую запись была последнего и пишет длинный или двойные переменные need not be atomic)
  • заказывающих: если запись видна, поэтому какая-либо запись, предшествующая его. Например, если один поток выполняет:

    x = new FancyObject(); 
    

    другой поток может читать x только после того, как конструктор FancyObject выполнил полностью.

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

Гонка данных, однако, не поставит под угрозу целостность виртуальной машины Java. В частности, JVM не будет выходить из строя или останавливаться, а также гарантировать безопасность памяти (т. Е. Предотвратить повреждение памяти) и certain semantics of final fields.

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