Кэширование потоков и модель памяти Java

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

Заранее благодарю

Ответ 1

Процессоры имеют разные уровни кэшей L1, L2, L3. Каждый процессор (а также/может иметь процессорное ядро) имеет собственный кеш. В этом кэше хранится минимальный набор основной памяти (ОЗУ) для производительности.

  _______________    ______________  
 |     CPU 1     |  |     CPU 2    |  
 |   _________   |  |   _________  |  
 |  | Level 1 |  |  |  | Level 1 | |  
 |  |   Cache |  |  |  |  Cache  | |  
 |  |         |  |  |  |         | |
 |  |_________|  |  |  |_________| |  
 |_______________|  |______________|
           | |              | |
           | |              | |
          _|_|______________|_|__
         |                       |
         |      MAIN MEMORY      | 
         |_______________________|


  Time     Command                 CPU 1 (Cache)      CPU 2 (Cache)        Main Memory     
-------  ----------              ----------------    --------------       -------------
  1          ---                       ---                ---                x = 10
  2       Read x  (on cpu1)           x = 10              ---                x = 10
  3       Write x <--20 (on cpu1)     x = 20              ---                x = 10       
  4       Read  x (on cpu2)           x = 20              x = 10             x = 10
  5       put cache to Main mem       x = 20              x = 10             x = 20

Например, над порядком выполнения, значение x неверно на CPU2. x уже изменено CPU1. Если переменная x определена как изменчивая, все операции записи мгновенно отражаются на основной памяти.

Ответ 2

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

Итак, если один поток пытается изменить переменную int, например, для некоторого объекта, он кэширует переменную int, и если она изменит ее, другой поток может не увидеть изменения.

Это правильно. Модель Java Memory определена в случае, когда она выполняется до правил, например. происходит прежде, чем правило между изменчивой записью поля x и изменчивым чтением поля x. Поэтому, когда запись выполнена, последующее чтение увидит записанное значение.

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

Если поток кэширует ссылку на объект, любое изменение состояния объекта также не видимо для других потоков? Почему?

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

Ответ 3

Процессоры имеют несколько кешей. Именно эти аппаратные кэши могут иметь непоследовательные копии данных. Причина, по которой они могут быть непоследовательными, заключается в том, что сохранение всех последовательных может замедлить ваш код в 10 раз и разрушить любую выгоду, получаемую от нескольких потоков. Чтобы получить достойную производительность, вы должны быть избирательно последовательными. Модель памяти Java описывает, когда она будет обеспечивать согласованность данных, но в простейшем случае это не так.

Примечание. Это не просто проблема с процессором. Поле, которое не должно согласовываться между потоками, может быть встроено в код. Это может означать, что если один поток изменяет значение, другой поток может НИКОГДА не видеть это изменение, поскольку оно было сжечь в коде.

Ответ 4

"Однако, прежде чем вы сможете написать достойный многопоточный код, вам действительно нужно больше изучить сложности и тонкости многопоточного кода.

Когда дело доходит до потоков, очень мало гарантируется.

Можете ли вы представить себе хаос, который может возникнуть, когда два разных потока имеют доступ к одному экземпляру класса, оба потока вызывают методы для этого объекта... и эти методы изменяют состояние объекта?... это слишком страшно, чтобы даже визуализировать. ", от Sun Certified Programmer для Java 6, глава 9: Темы.

Мой друг,

В Java потоки не кэшируют какой-либо объект или переменную, они просто имеют ссылку для экземпляра объекта. Говорить о потоковой кеш-памяти больше напоминает разговоры о действующих системах... Java работает одинаково во всех ОС, независимо от того, как потоки управляются внутри, что сильно отличается в зависимости от разных ОС.

Посмотрите этот код:

AccountDanger r = new AccountDanger();
Thread one = new Thread(r):
Thread two = new Thread(r);

Как вы можете видеть, в этом случае потоки имеют доступ к одному экземпляру: r. Тогда у вас будут проблемы с синхронизацией, конечно... не имеет значения, говорим ли мы о родных или объектных элементах, потоки 1 и 2 будут иметь доступ ко всем члены r (если они доступны через scope или seters/getters), и они будут читать непосредственно значения из r. Это точно, даже если вы этого не заметите, что иногда очень сложно.

Я рекомендую вам прочитать о java scopes и java synchronization, если вы хотите кодировать многопоточные приложения.

Привет,

Ответ 5

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

Однако многопоточные программы должны быть очень осторожны в обмене переменными (памятью), потому что очень легко ввести условия гонки, если вы не будете осторожны.