Разница между Data Race и Race Condition

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Давайте сначала разберемся с Data Race и Race Condition по отдельности.

The Java Language Specification говорит нам что когда программа содержит два конфликтующих доступа(например read и write), которые не упорядочены отношением "happens-before", говорят, что она содержит Data Race.

Довольно размытое определение, давайте лучше посмотрим что такое Data Race на практике.

int x // общий для всех потоков ресурс

thread1 {
  x = 1 // параллельно изменяет общий ресурс
}

thread2 {
  x = 2 // параллельно изменяет общий ресурс
}

thread3 {
  read x // параллельно читает общий ресурс
}

В этом случае мы можем прочитать 1, 2 или даже 0 из нашего поля х, это называется чтение через гонку.

В этом примере у нас есть Data Race, но нет Race Condition, чтобы избавиться от Data Race нам нужно упорядочить наши операции чтения и помощью отношения "happens-before".

Мы можем сделать это с помощью добавления ключевого слова "volatile", Это ключевое слово имеет семантику для видимости памяти. По сути, значение изменчивого поля становится видимым для всех считывателей (в частности, для других потоков) после завершения операции записи в него. Без volatile читатели могли бы увидеть некоторое не обновленное значение.

volatile int x // общий для всех потоков ресурс

thread1 {
  x = 1 // параллельно изменяет общий ресурс
}

thread2 {
  x = 2 // параллельно изменяет общий ресурс
}

thread3 {
  read x // параллельно читает общий ресурс
}

В этом случае наша программа не содержит Data Race и также не содержит Race Condition, а это значит, что наша программа (согласно Java Language Specification) "корректно синхронизованна".

Теперь поговорим о Race Condition

Java Concurrency на практике говорит нам, что "при Race Condition возникает возможность появления неправильных результатов из-за неудачной временной координации потоков".

Понятнее не стало... Ладно, посмотрим на практике.

volatile int x // общий для всех потоков ресурс

incrementx() {
  x++
}
getx() {
  return x
}

thread1 {
  incrementx() // параллельно инкрементирует общий ресурс
}
thread2 {
  incrementx() // параллельно инкрементирует общий ресурс
}
thread3 {
  incrementx() // параллельно инкрементирует общий ресурс
}

thread4 {
  getx() // параллельно читает общий ресурс
}

На этом примере наша программа не содержит Data Race, но содержит Race Condition, второй поток может "переписать" значение, записанное первым потоком. Для того чтобы решить эту проблему обычного volatile недостаточно, тут нам поможет синхронизация (или Atomic типы данных). Решим мы эту проблему с помощью клучевого слова synchronized.

volatile int x // общий для всех потоков ресурс

synchronized incrementx() {
  x++
}
getx() {
  return x
}

thread1 {
  incrementx() // параллельно инкрементирует общий ресурс
}
thread2 {
  incrementx() // параллельно инкрементирует общий ресурс
}
thread3 {
  incrementx() // параллельно инкрементирует общий ресурс
}

thread4 {
  getx() // параллельно читает общий ресурс
}

Кстати мы можно не делать synchronized метод getx(), так как поле х у нас volatile.

Сейчас рассмотрим вариант в котором наша программа имеет как Data Race, так и Race Condition.

int x // общий для всех потоков ресурс

incrementx() {
  x++
}
getx() {
  return x
}

thread1 {
  incrementx() // параллельно инкрементирует общий ресурс
}
thread2 {
  incrementx() // параллельно инкрементирует общий ресурс
}
thread3 {
  incrementx() // параллельно инкрементирует общий ресурс
}

thread4 {
  getx() // параллельно читает общий ресурс
}

Прочитать эта программа может все что угодно.

Под конец могу сказать, что гораздо полезнее выявлять Race Condition, чем Data Race, Data Race даже иногда может быть полезным и приводить к увеличению производительности (реализованно в String.class в Java).

Источник: https://habr.com/ru/articles/760434/


Интересные статьи

Интересные статьи

Во многих мобильных и веб-приложениях требуется создать чаты. Мы говорим не о надоевших всплывашках для общения с оператором: обычно они предоставляются As a service и подключаются одной строчкой кода...
Все вы знаете, что Flutter реализует несколько абстракций для передачи данных между Dart-кодом и кодом, связанным с оболочкой Flutter Engine на языке платформы (например, Kotlin для Android). Но в дей...
Вспомним сегодня о Меркурии. Я давно о нем не рассказывал. А ведь все это время Меркурий находился в значительном угловом удалении от Солнца, и мог был быть прекрасно виден… если бы мы находились ...
Вот и состоялся наш первый митап для специалистов по работе с данными — LaTech Data Talks. Мы рассказали о том, как устроена команда Data & Analytics в Lamoda и с какими вызовами нам приходится сп...
Неделю назад в наших соцсетях выступал Никита Александров — Data Scientist в Unity Ads, где он улучшает алгоритмы конверсии. Никита сейчас живет в Финляндии, и кроме прочего он расска...