Структура каталогов
1. Базовые знания о кеше ЦП
2. Попадание в кеш
3. Согласованность кеша
4. Типичные варианты использования
5. Псевдообмен очередями
Введение
По сути, знание кэша процессора является базовым знанием для входа на крупную фабрику, и это тоже очень важно, если вы хорошо освоите эту часть знаний, это будет бонусом!
Расскажите историю:
В первые десятилетия вычислительной техники основная память была невероятно медленной и невероятно дорогой, но и центральные процессоры не были особенно быстрыми. Начиная с 1980-х разрыв стал быстро увеличиваться. Тактовые частоты микропроцессоров взлетели до небес, но улучшение времени доступа к памяти было гораздо менее значительным. По мере увеличения этого разрыва становится все более очевидным, что для его преодоления необходим новый тип быстрой памяти.
1980 и ранее: процессор не имеет кеша
1980–1995: ЦП начал иметь кэш-память 2-го уровня.
Пока: L4, немного L0, обычно L1, L2, L3
Практическая тренировка
Основы кэш-памяти процессора
регистр(Регистр) — это память компьютера, используемая для временного хранения инструкций, данных и адресов в центральном процессоре. Емкость регистра ограничена, а скорость чтения и записи очень высока. В компьютерной архитектуре регистры хранят промежуточные результаты вычислений, выполненных в известные моменты времени, ускоряя выполнение компьютерных программ за счет быстрого доступа к данным.
Регистры находятся на самом верху иерархии памяти и являются самой быстрой памятью, которую ЦП может читать и записывать. Регистры обычно измеряются количеством битов, которые они могут содержать, например, 8-битный регистр или 32-битный регистр. В центральном процессоре компонентами, содержащими регистры, являются регистр команд (IR), программный счетчик и аккумулятор. Регистры теперь реализованы как массивы регистров, но они также могут быть реализованы с использованием отдельных триггеров, высокоскоростной основной памяти, тонкопленочной памяти и других методов на нескольких машинах.
регистрТакже может относиться к группе регистров, которые могут быть непосредственно проиндексированы выводом или вводом инструкции. Эти регистры более точно называются «архитектурными регистрами». Например, набор инструкций x86 определяет набор из восьми 32-битных регистров, но ЦП, реализующий набор инструкций x86, может иметь внутри более восьми регистров.
Кэш процессора
В компьютерной системе,Кэш процессора(английский: CPU Cache, именуемый в этой статье кэшем) — это компонент, используемый для сокращения среднего времени, необходимого процессору для доступа к памяти. Он расположен на втором нисходящем уровне пирамиды памяти после регистрации ЦП. Его емкость намного меньше памяти, но скорость может быть близка к частоте процессора.
Когда процессор выдает запрос на доступ к памяти, он сначала проверяет, есть ли данные запроса в кэше. Если он есть (попадание), то данные возвращаются напрямую, без обращения к памяти, если нет (сбой), то соответствующие данные в памяти должны быть сначала загружены в кэш, а затем возвращены в процессор.
Причина, по которой кеш эффективен, заключается главным образом в том, что доступ к памяти во время работы программы характеризуется локальностью. Эта локальность включает в себя как пространственную локальность (Spatial Locality), так и временную локальность (Temporal Locality). Используя преимущество этой локализации, кеши могут достигать чрезвычайно высоких показателей попадания.
С точки зрения процессора кэш является прозрачным компонентом. Поэтому программисты обычно не могут напрямую вмешиваться в работу кэша. Однако можно реализовать определенные оптимизации программного кода на основе характеристик кеша, чтобы лучше использовать кеш.
Современные компьютеры обычно имеютКэш 3-го уровня (L1, L2, L3), чтобы увидеть структуру:
в:
- Существует два типа кэш-памяти L1: кэш-память инструкций и кэш-память данных. Кэш L2 и кэш L3 не различают инструкции и данные.
- L1 и L2 кэшируются в каждом ядре ЦП, а L3 — это память, совместно используемая всеми ядрами ЦП.
- Чем ближе L1, L2 и L3 к ЦП, тем меньше и выше скорость, чем дальше от ЦП, тем медленнее скорость.
- Задняя часть памяти, задняя часть памяти жесткий диск
Давайте посмотрим на их скорость:
Взгляните на процессор, который я использую на работе, хотя он немного дрянной~
Для конкретной информации мы можем увидеть:
Скорость L1 примерно в 27-36 раз выше, чем у основной памяти.L1 и L2 относятся к уровню KB, L3 - к уровню M, а L1 разделен на кэши данных и инструкций по 32 КБ соответственно.Подумайте, почему нет L4?
Вот картинка
Эта диаграмма из обзора Haswell от Anandtech полезна, поскольку она иллюстрирует добавление огромных (128 МБ) Кэш L4и влияние традиционной структуры L1/L2/L3 на производительность. Каждая лестница представляет собой новый уровень кэша. Красная линия — чип с L4 — обратите внимание, что он по-прежнему почти в два раза быстрее, чем два других чипа Intel для больших файлов. Но большие кэши требуют больше транзисторов, которые медленны, дороги и увеличивают размер чипа.
Так есть L0?
Ответ: да. Современные ЦП также обычно имеют очень маленький кэш «L0», обычно размером всего несколько КБ, для хранения микроопераций. И AMD, и Intel используют этот кеш. Кэш Zen составляет 2048 µOP, а Zen 2 — 4096 µOP. Эти крошечные буферные пулы работают по тем же общим принципам, что и L1 и L2, но представляют собой дажеменьше памятипул, процессор может бытьМеньшая задержка, чем L1чтобы получить к ним доступ. Часто компании подстраивают эти функции друг под друга. Zen 1 и Zen+ (APU Ryzen 1xxx, 2xxx, 3xxx) имеют кэш инструкций L1 объемом 64 КБ, который является 4-канальным ассоциативным с кэшем L0 объемом 2048 µOP. Zen 2 (процессоры Ryzen 3xxx для настольных ПК, Ryzen Mobile 4xxx) имеет кэш инструкций L1 объемом 32 КБ, который является ассоциативным с 8 путями и имеет кэш-память 4096 µOP. Удвоение набора ассоциативности и размера кеша µOP позволяет AMD вдвое сократить размер кеша L1.
Сказав все это, как работает кеш процессора?
Назначение кэша процессора: мой процессор очень быстрый, и каждый раз, когда я обращаюсь к основной памяти для извлечения данных, стоимость слишком высока, поэтому я сам открываю пул памяти для хранения некоторых данных, которые мне нужны больше всего. . Какие данные можно загрузить в кеш процессора? Сложные расчеты и программный код.
Что, если я не найду нужные данные в пуле памяти L1? этопромах кеша
Что еще я могу сделать, перейдите на L2, чтобы найти его, некоторые процессоры используютВключенный дизайн кэша(это означает, что данные, хранящиеся в кеше L1, также будут продублированы в кеше L2), адругие процессоры взаимоисключающие(это означает, что два кеша никогда не обмениваются данными). Если данные не найдены в кеше L2, ЦП продолжает цепочку до L3 (обычно все еще на кристалле), затем L4 (если он существует) и основной памяти (DRAM).
Возникает другой вопрос: как найти его более эффективным?Невозможно, чтобы процессоры проходили рядом друг с другом.
попадание в кэш процессора
cache line
Строка кэша также называется блоком кэша, что означает, что ЦП загружает данные один за другим. Вообще говоря, минимальные загружаемые данные строки кэша составляют 64 байта = 16 32-битных целых чисел (есть также другие процессоры 32 байта и 128 байт), ниже приведен размер строки кэша процессора моего компьютера.
На рисунке выше кэш данных L1 моего процессора имеет размер 32 КБ:
32KBytes/64Bytes=512 Cache line
Стратегия отображения между кешем и памятью:
-
Хэш: (адрес памяти % строки кэша) * 64 склонен к конфликту хэшей
-
Ассоциативный набор N-Way: проще говоряРазделить N кэш-линий на группу и в каждой кэш-линии адресовать по смещению
Из приведенного выше рисунка видно, что кэш данных L1 размером 32 КБ разделен на 8 путей, тогда каждый путь равен 4 КБ.
Как решить эту проблему?
Мы знаем ранее: большинство строк кэша имеют размер 64 байта.
- Tag :Перед каждой строкой кэша будет независимо выделенный тег размером 24 бита = 3 байта для хранения, то есть первые 24 бита адреса памяти.
- Index: 6 бит = 3/4 байта за адресом памяти хранит индекс этой строки кэша.Через 6 бит мы можем индексировать 2 ^ 6 = 64 строки кэша.
- Offset: смещение строки кэша, хранящейся на 6 бит после индекса.
конкретный процесс:
- Используйте индекс, чтобы найти соответствующий блок кэша.
- Попытки с помощью тегов сопоставить соответствующее значение тега для этого блока кэша. Результат - попадание или промах.
- При попадании используйте смещение внутри блока, чтобы найти целевое слово в этом блоке. Затем перепишите слово напрямую.
- В случае промаха существует две стратегии обработки в зависимости от конструкции системы, а именно: выделение с записью и выделение без записи. Если он выделяется при записи, он сначала обрабатывается как промах чтения,Чтение пропущенных данных в кеш, а затем преобразовать данныеЗапись в читаемую единицу слова. Если он не выделен записью, напрямую передать данныезаписать обратно в память.
Что делать, если кэш определенного пути заполнен?
Замените некоторые из последних использованных байтов, которые часто называют LRU (самые длинные неиспользуемые)
После анализа кеша данных L1 вы также можете проанализировать другие кеши L2 и L3, поэтому я не буду его здесь анализировать.
когерентность кэша
Частично из Википедии:
Для обеспечения согласованности данных с хранилищем более низкого уровня (например, с памятью) обновления данных должны распространяться своевременно. Это распространение осуществляется путем обратной записи. Обычно существует две стратегии обратной записи:написать ответ(Написать ответ) инаписать через(Прописать).
В соответствии со стратегией обратной записи и стратегией распределения промахов, упомянутых выше, см. таблицу ниже.
Из рисунка выше мы знаем:
написать ответКогда: если кеш попадает, нет необходимости обновлять память, чтобы уменьшить операцию записи в память, обычно стратегия выделения заключается в выделении
-
Как пометить кеш как обновленный при загрузке другим процессором? Каждая строка Cache предоставляет грязный бит, чтобы определить, произошло ли обновление с момента его загрузки. (ЦП загружается по частям, а не побайтно, как упоминалось ранее)
написать через:
-
Сквозная запись означает, что всякий раз, когда кеш получает команду на запись данных, он напрямую записывает данные обратно в память. Если этот адрес данных также находится в кэше, кэш должен быть обновлен одновременно. Так как эта конструкция вызовет много операций записи в память, необходимо настроить буфер, чтобы уменьшить аппаратные конфликты. Этот буфер называется буфером записи (Write buffer), обычно не более 4-х размеров блока буфера. Однако буферы записи также могут использоваться для кэширования обратной записи с той же целью.
-
Сквозную запись легче реализовать, чем обратную запись, и она упрощает поддержание согласованности данных.
-
Обычно стратегия распределения не распределяется
Для двухуровневой системы кэширования в кэше L1 может использоваться сквозная запись для упрощения реализации, а в кэше L2 используется обратная запись для обеспечения согласованности данных.
Протокол МЭСИ:
Вот веб-страница (MESI Interactive Animations), этот адрес слишком 6x, я ссылаюсь на много информации, она все еще не так хороша, как анимация. . . .Woohoo. Протяните руку. TCD.IE/Jeremy. Jon E…
Рекомендуется сначала воспроизвести анимацию вышеуказанного URL-адреса, чтобы вы могли понять данные чтения и записи кэша и основной памяти каждого процессора.
Вот краткое пояснение: наша основная память имеет значение x=0, а процессор имеет два cpu0, cpu1
- cpu0 считывает значение x, cpu0 сначала ищет в кеше cpu0, но не может его найти, там одинадресная шина, заключается в маршрутизации процессора и основной памяти и переходе кпроцессор и оперативная память, сравнить версии, перейти в оперативную память для получения х, получить значение х черезШина данныхПрисвойте значение кешу cpu0
- cpu0 пишет в x+1, непосредственно получить x=0 для cpu0 и добавить 1 (здесь не будет обновляться основная память, а также не будет обновляться кеш cpu1, кеш cpu1 еще не имеет значения x)
- cpu1 считывает значение x, сначала найти его в кеше cpu1, но не могу найти, судя поадресная шина, перейти к процессору и основной памяти, чтобы найти одновременно, сравнить версию (если версия одинакова, она будет отдавать приоритет значению основной памяти), найти значение x для процессора0, проходы процессора0Шина данныхПриоритизируйте данные, чтобы обновить значение кэша x процессора1, а затем обновить значение основной памяти x
- процессор1 в х+1, напрямую получите x=1 для процессора 1 и добавьте 1 (здесь будет обновляться основная память и не будет обновляться кеш процессора 0, но будут уведомлены другие процессоры через RFO
В остальных случаях можете попробовать сами.
Соглашение об уведомлении:
Снупи протокол. Этот протокол больше похож на шинную технологию уведомления о данных. Кэш ЦП может идентифицировать статус данных в других кэшах через этот протокол. Если есть совместное использование данных, о состоянии общих данных можно уведомить другие кэши ЦП через механизм широковещательной рассылки. Этот протокол требует, чтобы каждый кэш ЦП мог «отслеживать» уведомления о событиях данных и реагировать соответствующим образом.
Статус протокола МЭСИ:
Modified (модифицированный), Exclusive (эксклюзивный), Shared (общий), Invalid (недействительный).
Следить за анимацией не очень сложно.
Чтобы немного расширить:
MOESI: MOESI — это полный протокол когерентности кэша, который содержит все возможные состояния, обычно используемые в других протоколах. В дополнение к четырем общим состояниям протокола MESI существует пятое состояние «владелец», которое представляет измененные и общие данные. Это позволяет избежать необходимости записывать измененные данные обратно в основную память перед совместным использованием. Несмотря на то, что данные в конечном счете все же должны быть записаны обратно, можноотложить обратную запись.
MOESF: данные в состоянии Forwardclean, можно отбросить ибез предупреждения
AMD использует MOESI, Intel использует MESIF
Я не буду продолжать здесь ~
Возьмите волну вариантов использования
Вариант использования 1:
public class CpuCache {
static int LEN = 64 * 1024 * 1024;
static int arr[] = new int[LEN]; // 64M
public static void main(String[] args) {
long currAddTwo = System.currentTimeMillis();
addTwo();
System.out.println(System.currentTimeMillis() - currAddTwo);
long currAddEight = System.currentTimeMillis();
addEight();
System.out.println(System.currentTimeMillis() - currAddEight);
}
private static void addTwo() {
for (int i = 0;i<LEN;i += 2) {
arr[i]*=i;
}
}
private static void addEight() {
for (int i = 0;i<LEN;i += 8) {
arr[i]*=i;
}
}
}
Вы можете догадаться, насколько или в несколько раз может отличаться напечатанное время.
Проанализируйте временную сложность: если addTwo равно 4n, то addEight равно n.
Но не забывайте, что строка кэша 64 байта загружается при загрузке ЦП, поэтому независимо от того, добавляют ли они 2 или добавляют 8, их время потребления одинаково.Потребление времени моей машины:
48
36
ложный обмен:
Цитируя немного измененный пример Мартина, код выглядит следующим образом:
public class FalseShare implements Runnable {
public static int NUM_THREADS = 2; // change
public final static long ITERATIONS = 500L * 1000L * 1000L;
private final int arrayIndex;
private static VolatileLong[] longs;
public FalseShare(final int arrayIndex) {
this.arrayIndex = arrayIndex;
}
public static void main(final String[] args) throws Exception {
Thread.sleep(1000);
System.out.println("starting....");
if (args.length == 1) {
NUM_THREADS = Integer.parseInt(args[0]);
}
longs = new VolatileLong[NUM_THREADS];
for (int i = 0; i < longs.length; i++) {
longs[i] = new VolatileLong();
}
final long start = System.currentTimeMillis();
runTest();
System.out.println("duration = " + (System.currentTimeMillis() - start));
}
private static void runTest() throws InterruptedException {
Thread[] threads = new Thread[NUM_THREADS];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new FalseShare(i));
}
for (Thread t : threads) {
t.start();
}
for (Thread t : threads) {
t.join();
// System.out.println(t);
}
}
public void run() {
long i = ITERATIONS + 1;
while (0 != --i) {
longs[arrayIndex].value = i;
}
}
public final static class VolatileLong {
public volatile long value = 0L;
public long p1, p2, p3, p4, p5, p6;//, p7, p8, p9;
}
}
Логика кода по умолчанию4 потока изменяют содержимое разных элементов массива, Тип элементаVolatileLong, существует только одно длинное целочисленное значение члена и 6 неиспользуемых длинных целочисленных членов. Значение установлено как volatile, чтобы сделать изменение значения видимым для всех потоков.
Когда я установил поток на 4: 50-я строка кода с 6 длинными целыми числами выполнялась 13 с, но только 9 с, когда было только 4 длинных целых числа, а когда 50-я строка была закомментирована, она выполнялась 24 с. Нашел этот результат теста немного странным.
Во-первых, давайте разберемся с определением псевдосовместного использования:
В программе на Java элементы массива также непрерывны в кэше.Смежные переменные-члены также загружаются в ту же строку кэша., Если несколько потоков работают с разными переменными-членами, если это одна и та же строка кэша, может возникнуть ложное совместное использование (False Sharing).
Проект Disruptor упоминается ниже (GitHub.com/l max-ex Производит…
один работает на процессореcore 1нить о желанииобновить переменную Xзначение, в то время как другое выполняется на процессореcore 2нить о желанииобновить переменную уЗначение . Однако эти две часто изменяемые переменные находятся вта же строка кэша, Два потока по очередиОтправить запросновости, возьми этоВладение строкой кэша.
На поверхности X и Y обрабатываются независимыми потоками, и между этими двумя операциями нет никакой связи.поделился строкой кэша, но все конкурентные конфликты возникают из-за обмена.
Согласно приведенному выше примеру кода, говоря простым языком, когда мы работаем с массивом,Объект размером 8 байт (32-разрядные системы) или 12 байт (64-разрядные системы)., если добавить6 длинных целых чисел = 48 байтов, так чтоИспользуйте одну строку кэша для разных объектов, вы можете избежать того, чтобы строка кэша часто отправляла сообщения RFO, чтобы совместно использовать строку кэша и снизить конкуренцию, так почему же протестированные нами данные неверны? Когда есть 6 лонгов, это занимает больше времени, чем 4 лонга.
Причина в том, что у нашей машины 2 ядра.Когда поток установлен на 2, 6 длинных строк становятся 4s, а 50-я строка закомментирована и становится 10s.
Таким образом, за счет заполнения строки кэша объект может максимально использовать одну строку кэша, чтобы уменьшить синхронизацию строки кэша.
Псевдообмен очередями
в JDKLinkedBlockingQueue, есть ссылка head, указывающая на начало очереди, и ссылка last, указывающая на конец очереди.И этот вид очереди часто используется в асинхронном программировании, и часто используются значения этих двух ссылок.Модификации разными потоками, но они, скорее всего, будут в одной строке кеша, поэтому происходит ложное совместное использование.Чем больше потоков, тем больше ядер, тем больше негативное влияние на производительность.
Но: Псевдо-совместное использование также не оптимизируется ради оптимизации, вGrizzly, он поставляется с LinkedTransferQueue, который отличается от LinkedTransferQueue, поставляемого с JDK 7. Разница заключается в использованииPaddedAtomicReferenceНа самом деле, для улучшения производительности параллелизма это неправильный метод кодирования и бессмысленный.
Нетти использовала PaddedAtomicReference для замены исходного узла и использовала метод заполнения для решения проблемы псевдо-совместного использования очереди, но позжеОтмена.
Суть AtomicReference и LinkedTransferQueue заключается в оптимистической блокировке.Эффективность оптимистической блокировки очень низкая в жесткой конкуренции.Оптимистическую блокировку следует использовать в неконкурентных сценариях.Оптимизация производительности в условиях жесткой конкуренции за оптимистическую блокировку — неправильное направление., потому что пессимистическую блокировку следует использовать, если требуется интенсивная конкуренция.
Padded-AtomicReference тоже ложное утверждение.Если поощряется конкуренция, то почему бы не использовать Lock + volatile.Если жесткой конкуренции нет, использование PaddedAtomicReference не имеет преимущества перед AtomicReference. Таким образом, использование Padded-AtomicReference — плохой трюк с кодированием.
Таким образом, в версии 1.8 логика пэда, связанная с LinkedTransferQueue, была удалена, и будет опубликован код версии 1.7 —
public class FalseShare implements Runnable {
public static int NUM_THREADS = 2; // change
public final static long ITERATIONS = 500L * 1000L * 1000L;
private final int arrayIndex;
private static VolatileLong[] longs;
public FalseShare(final int arrayIndex) {
this.arrayIndex = arrayIndex;
}
public static void main(final String[] args) throws Exception {
Thread.sleep(1000);
System.out.println("starting....");
if (args.length == 1) {
NUM_THREADS = Integer.parseInt(args[0]);
}
longs = new VolatileLong[NUM_THREADS];
for (int i = 0; i < longs.length; i++) {
longs[i] = new VolatileLong();
}
final long start = System.currentTimeMillis();
runTest();
System.out.println("duration = " + (System.currentTimeMillis() - start));
}
private static void runTest() throws InterruptedException {
Thread[] threads = new Thread[NUM_THREADS];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new FalseShare(i));
}
for (Thread t : threads) {
t.start();
}
for (Thread t : threads) {
t.join();
// System.out.println(t);
}
}
public void run() {
long i = ITERATIONS + 1;
while (0 != --i) {
longs[arrayIndex].value = i;
}
}
public final static class VolatileLong {
public volatile long value = 0L;
public long p1, p2, p3, p4, p5, p6;//, p7, p8, p9;
}
}
50 потоков борются за 10 объектов,LinkedBlockingQueue лучше, чем LinkedTransferQueue
В 1.7 было в разы быстрее, а в 1.8 почти так же.
Наконец, давайте поговорим о Disruptor
- Структура кольцевого массива
Чтобы избежать сборки мусора, используйтемножествоа не связанный список. В то же время массивмеханизм кэшированияболее дружелюбный.
- позиционирование элемента
Длина массива составляет 2^n, а скорость позиционирования увеличивается за счет побитовой операции. Индексы принимают форму увеличения. Не беспокойтесь о переполнении индекса. Индекс является длинным типом, и даже если используется скорость обработки в 1 миллион запросов в секунду, его использование займет 300 000 лет.
- беззамковая конструкция
Каждый поток-производитель или потребитель будет сначала запрашивать позицию рабочего элемента в массиве, а после приложения напрямую записывать или читать данные в этой позиции.
Давайте проигнорируем кольцевую структуру массива и представим, как реализовать схему без блокировок. Весь процесс проходит через атомарные переменныеCAS, чтобы обеспечить потокобезопасность операции.
Стратегия потребительского ожидания:
- BlockingWaitStrategy: сценарии, в которых блокирующих ресурсов ЦП недостаточно, а пропускная способность и задержка не важны.
- BusySpinWaitStrategy: Spin уменьшает количество системных вызовов, вызванных переключением потоков, и уменьшает задержку за счет постоянных повторных попыток. Рекомендуется для использования в сценариях, где потоки привязаны к фиксированному ЦП.
- PhasedBackoffWaitStrategy: spin + yield + пользовательская стратегия, ресурсы ЦП ограничены, пропускная способность и задержка не являются важными сценариями
- SleepingWaitStrategy: Spin + yield + sleep, хороший компромисс между производительностью и ресурсами ЦП. Неравномерная задержка
- TimeoutBlockingWaitStrategy: блокировка, есть ограничение по тайм-ауту, ресурсы ЦП ограничены, пропускная способность и задержка не являются важными сценариями (logfj2 использует эту стратегию по умолчанию)
- YieldingWaitStrategy: Spin + yield + spin, хороший компромисс между производительностью и ресурсами ЦП. Задержка даже
import com.lmax.disruptor.*;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import java.util.concurrent.ThreadFactory;
public class DisruptorMain
{
public static void main(String[] args) throws Exception
{
// 队列中的元素
class Element {
private String value;
public String get(){
return value;
}
public void set(String value){
this.value= value;
}
}
// 生产者的线程工厂
ThreadFactory threadFactory = new ThreadFactory(){
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "simpleThread");
}
};
// RingBuffer生产工厂,初始化RingBuffer的时候使用
EventFactory<Element> factory = new EventFactory<Element>() {
@Override
public Element newInstance() {
return new Element();
}
};
// 处理Event的handler
EventHandler<Element> handler = new EventHandler<Element>(){
@Override
public void onEvent(Element element, long sequence, boolean endOfBatch)
{
System.out.println("Element: " + element.get());
}
};
// 阻塞策略
BlockingWaitStrategy strategy = new BlockingWaitStrategy();
// 指定RingBuffer的大小
int bufferSize = 16;
// 创建disruptor,采用单生产者模式
Disruptor<Element> disruptor = new Disruptor(factory, bufferSize, threadFactory, ProducerType.SINGLE, strategy);
// 设置EventHandler
disruptor.handleEventsWith(handler);
// 启动disruptor的线程
disruptor.start();
RingBuffer<Element> ringBuffer = disruptor.getRingBuffer();
for (int l = 0; true; l++)
{
// 获取下一个可用位置的下标
long sequence = ringBuffer.next();
try
{
// 返回可用位置的元素
Element event = ringBuffer.get(sequence);
// 设置该位置元素的值
event.set(l+"rs");
}
finally
{
System.out.println("push" + sequence);
ringBuffer.publish(sequence);
}
Thread.sleep(10);
}
}
}
Опубликуйте тестовый пример! Дождитесь следующей статьи, чтобы объяснить это подробно! Следующая статья будет о Volatile, дайте мне отдохнуть~~
Добро пожаловать, чтобы исправить и обменять! !
Добро пожаловать в мою официальную учетную запись и мои наггетсы , некоторые последние статьи будут опубликованы первыми!
Ниже мой личный сайт:ransongv587.com:996
Популярная рекомендация:
В конце статьи мы недавно составили материал для интервью «Руководство по прохождению интервью по Java», в котором рассматриваются основные технологии Java, JVM, параллелизм Java, SSM, микросервисы, базы данных, структуры данных и многое другое. способ получения:GitHub GitHub.com/ting с -note…, и еще больше контента впереди.