Для установки нажмите кнопочку Установить расширение. И это всё.

Исходный код расширения WIKI 2 регулярно проверяется специалистами Mozilla Foundation, Google и Apple. Вы также можете это сделать в любой момент.

4,5
Келли Слэйтон
Мои поздравления с отличным проектом... что за великолепная идея!
Александр Григорьевский
Я использую WIKI 2 каждый день
и почти забыл как выглядит оригинальная Википедия.
Статистика
На русском, статей
Улучшено за 24 ч.
Добавлено за 24 ч.
Альтернативы
Недавние
Show all languages
Что мы делаем. Каждая страница проходит через несколько сотен совершенствующих техник. Совершенно та же Википедия. Только лучше.
.
Лео
Ньютон
Яркие
Мягкие

Из Википедии — свободной энциклопедии

Модель памяти Java (англ. Java Memory Model, JMM) описывает поведение потоков в среде исполнения Java. Модель памяти — часть семантики языка Java, и описывает, на что может и на что не должен рассчитывать программист, разрабатывающий ПО не для конкретной Java-машины, а для Java в целом.

Исходная модель памяти Java (к которой, в частности, относится «потоколокальная память»), разработанная в 1995 году, считается неудачной: многие оптимизации невозможно провести, не потеряв гарантию безопасности кода. В частности, есть несколько вариантов написать многопоточную «одиночку»:[1]

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

В J2SE 5.0 (30 сентября 2004) появилась новая модель памяти, разработанная через Java Community Process под названием JSR-133[2][3]. Она лучше отражала принципы работы современных процессоров и компиляторов, и другие языки брали идеи из модели Java. Основной вклад в её создание внесли Сарита Адве, Джереми Мейсон и Билл Пью[англ.][4].

Предпосылки

Язык программирования Java позволяет писать многопоточные программы. Поскольку Java может работать на самых разных процессорах и ОС, синхронизация потоков особенно затрудняется. Чтобы программист мог сделать какие-то выводы о поведении программ, разработчики Java решили чётко определить различные варианты поведения всех программ на Java.

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

К тому же в однопоточной среде достаточно потребовать от системы «псевдопоследовательного» выполнения программы — наблюдателю, который видит только ввод-вывод, будет казаться, что все действия выполняются в том порядке, в котором они появились в программе, даже если это не так. Однако любому, кто сможет «заглянуть» в память компьютера — в том числе другому потоку — все эти «трюки» будут заметны. Рассмотрим два потока, которые одновременно выполняют такой код (x и y изначально нули).

Поток 1 Поток 2
x = 1; int r1 = y;
y = 2; int r2 = x;

Если нет перестановок, а поток 2 считал y=2, гарантированно должно быть x=1: ведь запись в x выполняется прежде, чем запись в y. С перестановкой оказывается возможна и, казалось бы, парадоксальная ситуация: r1=2, r2=0.

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

Модель памяти

Правило № 1: однопоточные программы исполняются псевдопоследовательно. Это значит: в реальности процессор может выполнять несколько операций за такт, заодно изменив их порядок, однако все зависимости по данным остаются, так что поведение не отличается от последовательного.

Правило № 2: нет невесть откуда взявшихся значений. Чтение любой переменной (кроме не-volatile long и double, для которых это правило может не выполняться) выдаст либо значение по умолчанию (ноль), либо что-то, записанное туда другой командой.

И правило № 3: остальные события выполняются по порядку, если связаны отношением строгого частичного порядка «выполняется прежде» (англ. happens before).

«Выполняется прежде»

В двух потоках нашлась цепочка «выполняется прежде» (обозначена символом строгого частичного порядка ), поэтому println выведет корректное значение.

«Выполняется прежде» (англ. happens before) — отношение строгого частичного порядка (антирефлексивное, антисимметричное, транзитивное), введённое между атомарными командами (++ и -- не атомарны), придуманное Лесли Лэмпортом и не означающее «физически прежде». Оно значит, что вторая команда будет «в курсе» изменений, проведённых первой.

В частности, одно выполняется прежде другого для таких операций (список не исчерпывающий):

  • Синхронизация и мониторы:
    • Захват монитора (начало synchronized, метод lock) и всё, что после него в том же потоке.
    • Возврат монитора (конец synchronized, метод unlock) и всё, что перед ним в том же потоке.
      • Таким образом, оптимизатор может заносить строки в синхроблок, но не наружу.
    • Возврат монитора и последующий захват другим потоком.
  • Запись и чтение:
    • Любые зависимости по данным (то есть запись в любую переменную и последующее чтение её же) в одном потоке.
    • Всё, что в том же потоке перед записью в volatile-переменную, и сама запись.
    • volatile-чтение и всё, что после него в том же потоке.
    • Запись в volatile-переменную и последующее считывание её же[2][5]. Таким образом, volatile-запись делает с памятью то же, что возврат монитора, а чтение — то же, что захват[6]. А значит: если один поток записал в volatile-переменную, а второй обнаружил это, всё, что предшествует записи, выполняется раньше всего, что идёт после чтения; см. иллюстрацию.
      • Для объектных переменных (например, volatile List x;) столь сильные гарантии выполняются для ссылки на объект, но не для его содержимого.
  • Обслуживание объекта:
    • Статическая инициализация и любые действия с любыми экземплярами объектов.
    • Запись в final-поля в конструкторе[7] и всё, что после конструктора. Как исключение из всеобщей транзитивности, это соотношение happens-before не соединяется транзитивно с другими правилами и поэтому может вызвать межпоточную гонку[8].
    • Любая работа с объектом и finalize().
  • Обслуживание потока:
    • Запуск потока и любой код в потоке.
    • Зануление переменных, относящихся к потоку, и любой код в потоке.
    • Код в потоке и join(); код в потоке и isAlive() == false.
    • interrupt() потока и обнаружение факта останова.

Влияние

Из-за повсеместного внедрения многопоточных и параллельных систем потребовался инструментарий с чёткой семантикой. Модель памяти Java стала первой попыткой разработать исчерпывающую модель межпоточного взаимодействия для крупного языка программирования[9].

В C++03 единственное замечание о многопоточности — для volatile-переменных не проводить никаких оптимизаций, связанных с ускорением доступа. Этого тоже не хватало, чтобы задействовать всю мощь переставляющего компилятора/процессора и не получить ошибку, связанную с внеочередным выполнением какой-то команды. Сходная модель памяти вошла в C++11[10].

См. также

Примечания

  1. The "Double-Checked Locking is Broken" Declaration. Дата обращения: 21 июля 2013. Архивировано 6 марта 2012 года.
  2. 1 2 Goetz, Brian Fixing the Java Memory Model, Part 2 (24 февраля 2004). Дата обращения: 18 октября 2010. Архивировано из оригинала 8 января 2007 года.
  3. Jeremy Manson and Brian Goetz. JSR 133 (Java Memory Model) FAQ (февраль 2004). — «The Java Memory Model describes what behaviors are legal in multithreaded code, and how threads may interact through memory. It describes the relationship between variables in a program and the low-level details of storing and retrieving them to and from memory or registers in a real computer system. It does this in a way that can be implemented correctly using a wide variety of hardware and a wide variety of compiler optimizations.». Дата обращения: 18 октября 2010. Архивировано 4 сентября 2013 года.
  4. James Gosling, Bill Joy, Guy L. Jr. Steele, Gilad Bracha, Alex Buckley. The Java Language Specification, Java SE 7 Edition. — Pearson Education, 2013. — ISBN 978-0-13-326032-8.
  5. Synchronization and the Java Memory Model. Дата обращения: 21 июля 2013. Архивировано 11 мая 2013 года.
  6. The JSR-133 Cookbook. Дата обращения: 18 июля 2013. Архивировано из оригинала 25 сентября 2013 года.
  7. Ниоткуда, кроме как из конструктора, в final-поля писать нельзя.
  8. >рабочие заметки: Гарантии для final-полей. Дата обращения: 22 июля 2013. Архивировано 16 января 2015 года.
  9. Goetz, Brian Fixing the Java Memory Model, Part 1 (24 февраля 2004). Дата обращения: 17 февраля 2008. Архивировано из оригинала 13 августа 2009 года.
  10. Boehm, Hans Threads and memory model for C++. Дата обращения: 17 февраля 2008. Архивировано из оригинала 4 сентября 2013 года.

Ссылки

Эта страница в последний раз была отредактирована 10 марта 2024 в 15:09.
Как только страница обновилась в Википедии она обновляется в Вики 2.
Обычно почти сразу, изредка в течении часа.
Основа этой страницы находится в Википедии. Текст доступен по лицензии CC BY-SA 3.0 Unported License. Нетекстовые медиаданные доступны под собственными лицензиями. Wikipedia® — зарегистрированный товарный знак организации Wikimedia Foundation, Inc. WIKI 2 является независимой компанией и не аффилирована с Фондом Викимедиа (Wikimedia Foundation).