Ошибки согласования памяти и помехи от потоков
В чем разница между ошибками согласования памяти и помехами потоков?
Как использование синхронизации для их исключения отличается или нет? Пожалуйста, проиллюстрируйте пример. Я не мог получить это из учебника Java по солнцу. Любая рекомендация материала (ов) для чтения, чтобы понять это исключительно в контексте java, была бы полезна.
Ответы
Ответ 1
Ошибки согласованности памяти не могут быть поняты исключительно в контексте java - детали поведения разделяемой памяти в системах с несколькими процессорами очень специфичны для архитектуры и ухудшают ее, x86 (где большинство людей, кодирующих сегодня, научились кодировать), имеет довольно понятную для программистов семантику по сравнению с архитектурами, которые были разработаны для многопроцессорных машин с самого начала (например, POWER и SPARC), поэтому большинство людей действительно не привыкли думать о доступе к памяти семантика.
Я приведу общий пример того, где ошибки целостности памяти могут вызвать у вас проблемы. Предположим в этом примере, что начальное значение x
равно 3. Почти все архитектуры гарантируют, что если один процессор выполняет код:
STORE 4 -> x // x is a memory address
STORE 5 -> x
и другой процессор выполняет
LOAD x
LOAD x
будет либо видеть 3,3
, 3,4
, 4,4
, 4,5
, либо 5,5
с точки зрения двух своих инструкций LOAD
. В принципе, процессоры гарантируют, что порядок записи в одну ячейку памяти поддерживается с точки зрения всех процессоров, даже если точное время, когда каждая из этих записей станет известно другим процессорам, может меняться.
В тех случаях, когда процессоры отличаются друг от друга, они имеют гарантии, что они выполняют операции LOAD
и STORE
с различными адресами памяти. Предположим в этом примере, что начальные значения как x
, так и y
равны 4.
STORE 5 -> x // x is a memory address
STORE 5 -> y // y is a different memory address
тогда другой CPU выполняет
LOAD x
LOAD y
В этом примере на некоторых архитектурах второй поток может видеть 4,4
, 5,5
, 4,5
, OR 5,4
. Ой!
Большинство архитектур занимают память с зернистостью 32- или 64-битного слова - это означает, что на 32-битной машине POWER/SPARC вы не можете обновить 64-разрядную целую память и безопасно прочитать ее из другого нить без явной синхронизации. Гуфи, да?
Помехи резьбы намного проще. Основная идея заключается в том, что java не гарантирует, что один оператор java-кода выполняется атомарно. Например, приращение значения требует считывания значения, увеличения его и последующего его сохранения. Таким образом, вы можете иметь int x = 1
после выполнения двух потоков x++
, x
может закончиться как 2
или 3
в зависимости от того, как чередуется чередование кода нижнего уровня (предположительно выглядит абстрактный абстрактный код на этом уровне как LOAD x, INCREMENT, STORE x
). Основная идея здесь заключается в том, что java-код разбивается на более мелкие атомные фрагменты, и вы не можете делать предположения о том, как они чередуются, если вы явно не используете примитивы синхронизации.
Для получения дополнительной информации, просмотрите эту статью. Это долго и сухо и написано пресловутой мудаком, но эй, это тоже хорошо. Также проверьте этот (или просто Google для "двойной проверки блокировки не работает" ). Эти проблемы с переупорядочиванием памяти вызвали уродливые головы для многих программистов на С++/java, которые несколько лет назад пытались немного уменшить их инициализацией синглтона.
Ответ 2
Интерференция потоков связана с тем, что потоки перезаписывают друг друга (скажем, поток А, увеличивающий счетчик и поток B, уменьшающий его одновременно), что приводит к ситуации, когда фактическое значение счетчика непредсказуемо. Вы избегаете их, применяя эксклюзивный доступ, по одному потоку за раз.
С другой стороны, несоответствие памяти - это видимость. Thread A может увеличивать counter
, но тогда поток B может не знать об этом изменении, поэтому он может прочитать некоторое предварительное значение. Вы избегаете их, устанавливая связь между событиями, которая находится
является просто гарантией того, что память, записанная одним конкретным оператором, видима для другого конкретного оператора. (per Oracle)
Ответ 3
В этой статье мы рассмотрим "Модели памяти: пример переосмысления параллельных языков и аппаратных средств" Adve и Boehm в августе 2010 года. 53 номер 8 сообщения ACM. Это доступно онлайн для членов Ассоциации для компьютерных машин (http://www.acm.org). Это касается проблемы в целом, а также обсуждает модель памяти Java.
Для получения дополнительной информации о модели памяти Java см. http://www.cs.umd.edu/~pugh/java/memoryModel/
Ответ 4
Проблемы с сохранением памяти обычно проявляются, как только что происходит - до отношений.
Time A: Thread 1 sets int i = 1
Time B: Thread 2 sets i = 2
Time C: Thread 1 reads i, but still sees a value of 1, because of any number of reasons that it did not get the most recent stored value in memory.
Вы предотвращаете это, используя ключевое слово volatile
для переменной или используя классы AtomicX из пакета java.util.concurrent.atomic
. Любое из этих сообщений гарантирует, что ни один второй поток не увидит частично измененное значение, и никто никогда не увидит значение, которое не является самым текущим реальным значением в памяти.
(Синхронизация getter и setter также устранит проблему, но может показаться странным для других программистов, которые не знают, почему вы это сделали, а также могут разбиться перед лицом таких вещей, как привязка фреймворков и инфраструктур персистентности, которые используют отражение.)
-
Чередование потоков - это когда два потока перемещают объект вверх и видят несогласованные состояния.
У нас есть объект PurchaseOrder с itemQuantity и itemPrice, автоматическая логика генерирует итоговую сумму счета.
Time 0: Thread 1 sets itemQuantity 50
Time 1: Thread 2 sets itemQuantity 100
Time 2: Thread 1 sets itemPrice 2.50, invoice total is calculated $250
Time 3: Thread 2 sets itemPrice 3, invoice total is calculated at $300
В потоке 1 был выполнен неверный расчет, потому что какой-то другой поток возился с объектом между его операциями.
Вы решаете эту проблему либо с помощью ключевого слова synchronized
, чтобы убедиться, что только один человек может выполнить весь процесс за раз, либо поочередно с блокировкой из пакета java.util.concurrent.locks
. Использование java.util.concurrent обычно является предпочтительным подходом для новых программ.
Ответ 5
1. Вмешательство резьбы
class Counter {
private int c = 0;
public void increment() {
c++;
}
public void decrement() {
c--;
}
public int value() {
return c;
}
}
Предположим, что существуют два потока Thread-A и Thread-B, работающие на один экземпляр счетчика. Say Thread-A вызывает increment(), и на то же время Thread-B вызывает декремент(). Thread-A считывает значение c и увеличьте его на 1. В то же время Thread-B считывает значение ( который равен 0, потому что добавочное значение еще не задано Thread-A), уменьшает его и устанавливает равным -1. Теперь Thread-A устанавливает значение 1.
2. Ошибки согласования памяти
Согласование памяти Ошибки возникают, когда разные потоки противоречивые представления об общих данных. В вышеуказанном счетчике, Допустим, что в одном экземпляре счетчика есть два потока, вызывает метод инкремента, чтобы увеличить значение счетчика на 1. Вот это не гарантирует, что изменения, сделанные одним потоком, будут видны другой.
Для большего посещения this.
Ответ 6
Во-первых, обратите внимание, что ваш источник НЕ является лучшим местом для изучения того, что вы пытаетесь изучить. Вы будете хорошо читать статьи из ответа @blucz (а также его ответ в целом), даже если это выходит за рамки Java. Oracle Trails не плохи сами по себе, но они упрощают вопросы и замаскивают их, поэтому вы можете обнаружить, что не понимаете, что вы только что узнали, или полезно или нет, и сколько.
Теперь, пытаясь ответить в первую очередь в контексте Java.
Помехи резьбы
происходит, когда операции потока чередуются, т.е. смешиваются. Нам нужны два исполнителя (потоки) и общие данные (место для вмешательства).
Изображение Daniel Stori, с сайта turnoff.us:
![Daniel Stori, turnoff.us]()
На изображении вы видите, что два потока в процессе GNU/Linux могут мешать друг другу. Потоки Java - это, по сути, объекты Java, указывающие на собственные потоки, и они также могут мешать друг другу, если они работают с одними и теми же данными (например, здесь, где "Рик" испортил рисунок данных своего младшего брата).
Ошибки согласования памяти - MCE
Ключевыми моментами здесь являются видимость памяти, происходит до и - воспитывается с помощью @blucz, аппаратного обеспечения.
MCE - это, очевидно, ситуации, когда память становится непоследовательной. На самом деле это термин для людей - для компьютеров память всегда совместима (если только она не повреждена). "Несоответствия" - это то, что люди "видят", потому что они не понимают, что именно произошло, и ожидали чего-то еще. "Почему это 1? Это должно быть 2?!"
Эта "воспринимаемая несогласованность", этот "пробел", относится к видимости памяти, то есть к тому, что видят разные потоки, когда они смотрят на память. И поэтому эти течения действуют.
Видите ли, в то время как чтение и запись в память являются линейными, когда мы рассуждаем о коде (особенно, когда думаем о том, как это выполняется по строкам)... на самом деле это не так. Особенно, когда задействованы потоки. Итак, прочитанное вами учебное пособие дает пример приращения счетчика двумя потоками и как поток 2 читает то же значение, что и поток 1. Фактические причины несоответствий в памяти могут быть связаны с оптимизацией, выполняемой с вашим кодом, с помощью javac, JIT или аппаратного обеспечения (то есть то, что люди ЦП сделали, чтобы ускорить их процессор и сделать его более эффективным). Эти оптимизации включают в себя предварительные хранилища, предсказание ветвей и теперь вы можете думать о них как о переупорядочивающем коде, чтобы в итоге он работал быстрее и использует/тратит меньше циклов процессора. Однако, чтобы убедиться, что оптимизация не выходит из-под контроля (или слишком далеко), некоторые гарантии сделаны. Эти гарантии образуют связь "случится раньше", где мы можем сказать, что до этого момента и после событий "случилось раньше". Представьте, что вы ведете вечеринку и помните, что Том добрался до Сьюзи, потому что вы знаете, что Роб пришел за томом и до Сьюзи. Роб - это событие, которое вы используете для формирования, происходит до отношений до событий, когда Том/Сюзи приходит.
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility
Ссылка выше говорит вам больше о видимости памяти и о том, что устанавливается - до отношений на Java. Это не будет сюрпризом, но:
- синхронизация
- начало потока
- присоединение к потоку
- ключевое слово volatile сообщает вам, что происходит запись - перед последующими чтениями, то есть, который читает ПОСЛЕ записи, не будет переупорядочиваться, чтобы быть "до", так как это нарушит отношения "произойдет-до".
Поскольку все, что касается памяти, имеет важное значение. У вашей платформы есть свои собственные правила, и в то время как JVM пытается сделать их универсальными, делая все платформы одинаковыми, только это означает, что на платформе A будет больше барьеров памяти, чем на платформе B.
Ваши вопросы
В чем разница между ошибками согласования памяти и помехами потоков? MCE - это видимость памяти для программирования потоков и НЕ имеет происходит-до отношения между чтением и записью, поэтому существует разрыв между тем, что люди считают "должно быть" и тем, что "на самом деле".
Интерференция потоков связана с перекрытием потоков, перемежением, перемежением и касанием общих данных, привинчиванием их к процессу, что может привести к тому, что поток A будет иметь красивый рисунок, уничтоженный потоком B. Вмешательство, являющееся вредным, обычно обозначает критический раздел, который почему синхронизация работает.
Как использование синхронизации для их исключения отличается или нет?
Прочтите также о тонких замках, жировых замках и конфликтах с резьбой.
Синхронизация во избежание вмешательства потока приводит к тому, что только один поток обращается к критическому разделу, другой поток блокируется (дорогостоящий, конфликт потоков). Когда дело доходит до синхронизации MCE, это происходит, прежде чем, когда дело доходит до блокировки и разблокировки мьютекса, см. Предыдущую ссылку на описание пакета java.util.concurrent.
Примеры: см. оба предыдущих раздела.