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

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

написать впереди

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

Все дизайны исходят из жизни, последняя главаЧтобы научиться параллельному программированию, необходимо глубокое понимание этих трех основных элементов.Было сказано, что как «капиталист», вы должны как можно больше сжимать оставшееся значение ЦП, памяти и ввода-вывода, но скорость выполнения задач между этими тремя сильно различается, ЦП > память > точки ввода-вывода, ЦП это небо, то память это земля, память это небо, IO это земля, так как же сбалансировать три и улучшить общую скорость?

  1. ЦП увеличивает кеш, и есть более одного слоя кеша, что уравновешивает медлительность памяти.
  2. ЦП может работать больше и сбалансировать разницу в скорости ввода-вывода за счет мультиплексирования с разделением времени.
  3. Инструкции по оптимизации компиляции

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

Три главных вопроса

видимость

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

Когда дело доходит до видимости, мы должны сначала ввести концепцию JMM (модель памяти Java), то есть модель памяти Java.Модель памяти Java предусматривает, что все переменные хранятся восновная память, когда поток использует переменную, переменная в основной памяти будеткопироватьна свое рабочее место илиличная память, когда поток читает и записывает переменную, он оперирует этой переменной в своей собственной рабочей памяти.

Приведенное выше описание легко понять с помощью рабочего процесса Git,Удаленный репозиторий Git — это основная память, а локальный репозиторий Git — собственная рабочая память.

Текстовое описание немного абстрактно, проиллюстрируем его:

Посмотрите эту сцену:

  1. В основной памяти есть переменная x, начальное значение равно 0
  2. Поток A хочет увеличить x на 1, сначала скопирует x=0 в свою личную память, а затем обновит значение x.
  3. Время, за которое поток A сбрасывает обновленное значение x обратно в основную память, не фиксировано.
  4. Просто когда поток A не сбрасывает x обратно в основную память, поток B также считывает x из основной памяти, который в это время равен 0, та же операция, что и поток A, последний ожидаемый x=2 запрограммирует x=1

Это проблема с видимостью потока

JMM — это абстрактное понятие, в реальной реализации рабочая память потока выглядит так:

Чтобы сбалансировать короткую плату памяти / ввода-вывода, к ЦП будет добавлен кеш, Каждое ядро ​​имеет только свой собственный кеш первого уровня и даже имеет кеш второго уровня, общий для всех ЦП, как показано на рисунке выше. Говорят, что этот дизайн Студенты, изучающие аппаратное обеспечение, оставляют яму для студентов, изучающих программное обеспечение, но то, сможете ли вы перепрыгнуть через эту яму, также является ключевым показателем для измерения того, движутся ли студенты, изучающие программное обеспечение, к продвинутому Java...

намекать

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

Одним словом, чтобы решить проблему многопоточной видимости, все потоки должны сбрасывать переменные в основную память.Как решить проблему видимости? Ключевые слова JavavolatileПомогите вам это сделать, последующие главы будут анализировать...

атомарность

Атом относится к элементарным частицам, которые неразделимы в химических реакциях.Вы должны уметь чувствовать смысл атомарных операций:

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

В скетче «Почасовик» есть очень классическая строчка: сколько шагов нужно, чтобы положить слона в холодильник?

Давайте посмотрим на небольшую программу:

В случае многопоточности мы можем получить то, что ожидаемcount = 20000ценность? Некоторые студенты могут подумать, что метод счетчика, вызываемый потоком, имеет только одну операцию count++, которая является единственной операцией, поэтому она является атомарной, а не атомарной. В первой лекции ветки я сказал, что мы не можем использовать языковое мышление высокого уровня, чтобы понять метод обработки ЦП.Для преобразования count++ в инструкции ЦП требуется три шага.Следующие команды используются для разбора инструкций сборки и Дополнительная информация:

javap -c UnsafeCounter 

Перехватите инструкции по сборке метода счетчика, чтобы увидеть:

Объясните приведенную выше команду, 16: получить текущее значение счетчика и поместить его на вершину стека. 19 : положить константу 1 на вершину стека 20: Добавьте два значения, которые в данный момент находятся на вершине стека, и поместите результат на вершину стека. 21: переназначить результат вершины стека для подсчета

Видно, что простой count++ не является одношаговой операцией и не является атомарным после преобразования в сборку, это как слон, упаковывающий холодильник, на самом деле его нужно разбить на три шага:

Первый шаг - открыть дверцу холодильника, второй шаг - посадить внутрь слона, третий шаг - открыть дверцу холодильника.

В сочетании с пониманием структурной схемы JMM объясните, почему трудно получитьcount=20000результат:

Многопоточный счетчик, как обеспечить атомарность нескольких операций? Самый грубый способ - добавить к методуsynchronizedключевые слова, например:

Проблема решена.Если синхронизированный - панацея, то параллелизма может быть не так уж и много.Вы можете рассчитывать на синхронизированный, чтобы ходить по миру.Это не тот случай.Синхронизированный-это эксклюзивная блокировка (можно вызвать только один поток в то же время) поток, который не получит блокировку, будет заблокирован; кроме того, это также принесет много контекстных накладных расходов на переключение потоков

Итак, есть неблокирующий CAS (Сравните иSwap) реализует атомарные операции класса AtomicLong и других классов инструментов, учащиеся, прочитавшие исходный код, могут найти общую черту, все атомарные классы имеют следующий фрагмент кода:

private static final Unsafe unsafe = Unsafe.getUnsafe();

Этот класс предоставляется классом Unsafe в пакете rt.jar JDK.аппаратный уровеньАтомарная работа класса, методы в классе все изменены нативными, и несколько методов в этом классе будут объяснены перед введением атомарного класса, вот краткое введение, чтобы получить представление.

Некоторые студенты не понимают, что я только что упомянул о больших накладных расходах на переключение контекста потока.Вы можете понять это на двух примерах:

  • Вы (CPU) читаете две книги (два потока), коротко читаете первую книгу и затем переходите ко второй книге, коротко читаете вторую книгу и затем возвращаетесь к первой книге, и точно запоминаете какие строки вы видели, что вы видели сначала (ЦП запоминает информацию на уровне потока), когда спрашивали вас"в то же время"Если вы прочитаете 10 и более, стоимость переключения будет огромной.
  • В варьете есть много игр, которые позволяют вам считать деньги и одновременно заниматься другими делами, и, наконец, убедиться, что различные вещи выполняются правильно, мозговые накладные расходы невелики, вы узнаете, когда попробуете это 😊

упорядоченность

В вашей жизни, когда вы здороваетесь с другими, «Вы ели?» и «Вы ели?» означают одно и то же. Вы пишете следующую программу:

a = 1;
b =  2;
System.out.println(a);
System.out.println(b);

После оптимизации компилятора может стать так:

b =  2;
a = 1;
System.out.println(a);
System.out.println(b);

В этом случае компилятор корректирует порядок операторов и не оказывает никакого влияния, но компиляторнесанкционированныйОптимизация порядка заложила для нас мины, такие как синглтон, реализованный путем двойной проверки

Все идеально, не так ли, нет, проблема в том,instance = new Singleton();, эта 1 строка кода преобразуется в 3 инструкции ЦП, и мы понимаем, что новый объект должен быть таким:

  1. Выделить блок памяти M
  2. Инициализировать объект Singleton в памяти M
  3. Затем адрес M присваивается переменной экземпляра

но компиляторнесанкционированныйПосле оптимизации может стать так:

  1. Выделить блок памяти M
  2. Затем присвойте адрес M переменной экземпляра
  3. Инициализировать объект Singleton в памяти M

Во-первых, новый объект разбивается на три шага, оставляя за процессором возможность переключать потоки, кроме того, оптимизированная компилятором последовательность может привести к проблемам, см.:

  1. Поток A сначала выполняет метод getInstance, а когда выполняется инструкция 2, происходит переключение потока.
  2. Поток B только что ввел метод getInstance, чтобы определить, пуст ли экземпляр инструкции if.
  3. Поток A присвоил адрес M переменной экземпляра, поэтому поток B считает, что экземпляр не пуст.
  4. Поток B напрямую возвращает переменную экземпляра
  5. ЦП переключается обратно на поток A, и поток A завершает последующее содержимое инициализации.

Нарисуем картинку для наглядности:

Если поток A выполняет шаг 2 и поток переключается, поскольку поток A не выполняет красную стрелку полностью, поток B получит неинициализированный объект, и при доступе к переменной-члену экземпляра может произойти NPE, volatile или окончательная модификация (с участием класса механизм заряжания, см. мою предыдущую статью:Модель родительского делегирования: часто задаваемые вопросы о крупных фабриках, которые легко решить), проблема решена.

Суммировать

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

В этом разделе объясняются три проблемы. Следующие статьи также будут анализировать решения вышеуказанных проблем один за другим, а также относительно оптимальные решения. Пожалуйста, продолжайте обращать внимание. Кроме того, я загружу тестовый код о параллелизме на github как пример, и официальная учетная запись ответит «демо» »—> параллелизм для большего количества контента

вопрос души

  1. Почему переменные изменяются с помощью final потокобезопасного?
  2. Часто ли вы смотрите инструкции по сборке процессора?
  3. Если бы вас попросили написать синглтон, какую реализацию вы бы обычно использовали?

инструменты повышения производительности

Material Theme UI

Это плагин для темы IDEA. После установки выберитеMaterial Palenightтему и выполните следующие настройки

После настройки ваша ИДЕЯ выглядит следующим образом, вызывая чрезвычайный комфорт

Рекомендуемое чтение


Добро пожаловать, чтобы продолжать обращать внимание на общественный номер: «Сун Гун И Бин».

  • Передовая технология Java для обмена галантереей
  • Резюме эффективных инструментов | Ответ на «Инструменты»
  • Анализ вопроса интервью и ответ
  • Сбор технических данных | Ответ на «данные»

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