инструкция
Тогда в последний разРазорвите вопросы интервью ThreadLocal! ! !Когда интервьюер это услышал, эй, это неплохо! Эта статья продолжит тему выше и расскажет о FastThreadLocal,В настоящее время многие статьи о FastThreadLocal немного устарели и устарели (эта статья прояснит несколько недоразумений), а многие статьи о FastThreadLocal неполны.Надеюсь, эта статья поможет вам досконально разобраться в FastThreadLocal! ! !
FastThreadLocal предоставляется Netty и участвует в распределении памяти в пуле и так далее!
Что касается FastThreadLocal, Zero Degree готова объяснить следующие аспекты:
- Использование FastThreadLocal.
- FastThreadLocal не быстр во всех ситуациях, вы будете быстры, если будете использовать его правильно.
- FastThreadLocal использует вставку байтов для решения проблемы ложного совместного использования.
- FastThreadLocal быстрее, чем ThreadLocal, а не по времени.
- FastThreadLocal больше не использует ObjectCleaner для обработки утечек, и при необходимости рекомендуется переопределить метод onRemoval.
- Почему FastThreadLocal работает быстро?
Использование FastThreadLocal
Использование FastThreadLocal совместимо с ThreadLocal
FastThreadLocal использует пример кода:
public class FastThreadLocalTest {
private static FastThreadLocal<Integer> fastThreadLocal = new FastThreadLocal<>();
public static void main(String[] args) {
//if (thread instanceof FastThreadLocalThread) 使用FastThreadLocalThread更优,普通线程也可以
new FastThreadLocalThread(() -> {
for (int i = 0; i < 100; i++) {
fastThreadLocal.set(i);
System.out.println(Thread.currentThread().getName() + "====" + fastThreadLocal.get());
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "fastThreadLocal1").start();
new FastThreadLocalThread(() -> {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "====" + fastThreadLocal.get());
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "fastThreadLocal2").start();
}
}
Скриншот кода:
Результат запуска кода:
Давайте рассмотрим предыдущий ThreadLocalЛучшие практики:
try {
// 其它业务逻辑
} finally {
threadLocal对象.remove();
}
Примечание:В приведенном выше примере мы обнаружили, что FastThreadLocal и ThreadLocal в основном схожи в использовании, и особой разницы между ними нет.Лично в этом преуспевает FastThreadLocal: пользователи могут использовать его так же, как и ThreadLocal, и он должен быть совместим!
При использовании FastThreadLocal не нужно сначала пытаться, как ThreadLocal ……………… Затем, наконец, threadLocal object.remove();
Поскольку FastThreadLocalThread создан, объект Runnable обертывается FastThreadLocalRunnable:
FastThreadLocalRunnable.wrap(target)从而构造了FastThreadLocalRunnable对象。
FastThreadLocalRunnable вызовет FastThreadLocal.removeAll() после выполнения;
Примечание:FastThreadLocal больше не использует ObjectCleaner для обработки утечек, и при необходимости рекомендуется переопределить метод onRemoval. Эта часть будет представлена позже в этой статье, поэтому многие онлайн-материалы относительно старые, и эта часть была удалена.
Если это обычный поток, это все равно должно быть лучшей практикой:
наконец { объект fastThreadLocal.removeAll(); }
Уведомление:Если вы используете FastThreadLocal, не используйте обычные потоки, а создавайте FastThreadLocalThread.Почему это так, будет рассказано далее в этой статье: FastThreadLocal не быстр во всех ситуациях, вы будете быстры, если будете использовать его правильно.
FastThreadLocal не быстр во всех ситуациях, он будет быстрым, если вы используете его правильно
Сначала взгляните на тестовый пример netty для этой части: путь кода:GitHub.com/Нетти/Нетти…
Примечание:В моем локальном тесте пропускная способность FastThreadLocal примерно в 3 раза выше, чем у jdkThreadLocal. Машина другая, и эффект может быть разный, можете сами попробовать, все равно намного быстрее.
Что касается ThreadLocal, предыдущий:Разорвите вопросы интервью ThreadLocal! ! !было подробно описано.
FastThreadLocal работает не во всех ситуациях, вы будете быстры, если будете использовать его правильно! ! !
Уведомление:Использование потока FastThreadLocalThread будет быстрее, если это обычный поток, то еще медленнее!Уведомление:Использование потока FastThreadLocalThread будет быстрее, если это обычный поток, то еще медленнее!Уведомление:Использование потока FastThreadLocalThread будет быстрее, если это обычный поток, то еще медленнее!
В тестовом каталоге netty есть 2 класса:
- FastThreadLocalFastPathBenchmark
- FastThreadLocalSlowPathBenchmark
дорожка:GitHub.com/Нетти/Нетти…
Результаты теста FastThreadLocalFastPathBenchmark:Это примерно в 3 раза больше пропускной способности ThreadLocal.
Результаты теста FastThreadLocalSlowPathBenchmark:Более низкая пропускная способность, чем ThreadLocal.
Вывод теста: Использование потока FastThreadLocalThread для работы FastThreadLocal будет быстрее, если это обычный поток, он будет медленнее!
В примечании три пункта:
-
Когда FastThreadLocal работает с элементами, он использует константные индексы для поиска элементов в массиве вместо ThreadLocal через хеширование и хеш-таблицы Это изменение особенно эффективно при частом использовании!
-
Чтобы воспользоваться перечисленными выше функциями, поток должен быть FastThreadLocalThread или его подклассами.По умолчанию DefaultThreadFactory использует FastThreadLocalThread.
-
FastThreadLocal быстрее использовать только для потоков FastThreadLocalThread или подклассов, поскольку FastThreadLocalThread определяет тип свойства threadLocalMap как InternalThreadLocalMap. Если обычные потоки будут использовать ThreadLocal.
Давайте посмотрим на детали NioEventLoopGroup:
Видя это, он согласуется с содержанием комментариев, которые мы только что видели, то есть используется FastThreadLocalThread.
Обычно используются примеры использования FastThreadLocal в netty:
Выделение объединенной памяти:
Буду использовать Recycler
И Recycler также использует FastThreadLocal
Давайте снова посмотрим на тестовый класс:
Примечание:Мы обнаружим, что поток в FastThreadLocalFastPathBenchmark — это FastThreadLocal.
Примечание:Найдем потоки в FastThreadLocalSlowPathBenchmarkне FastThreadLocal.
FastThreadLocal работает быстрее только тогда, когда используется поток FastThreadLocalThread или его подклассы.Пропускная способность, которую я тестировал здесь, примерно в 3 раза, но если это обычный поток, работающий с FastThreadLocal, его пропускная способность хуже, чем ThreadLocal!
FastThreadLocal использует вставку байтов для решения проблемы ложного обмена
Контент о кеше ЦП исходит от Meituan:Специальности.Meituan.com/2016/11/18/…
На рисунке ниже показана базовая структура расчета. L1, L2 и L3 представляют кэш первого уровня, кэш второго уровня и кэш третьего уровня соответственно.Чем ближе к кешу ЦП, тем выше скорость и меньше емкость. Таким образом, кэш L1 небольшой, но быстрый и используется рядом с ядром ЦП; L2 больше и медленнее и по-прежнему используется только одним ядром ЦП; L3 больше и медленнее и используется одним ядром ЦП. на сокете и, наконец, основная память, совместно используемая всеми ядрами ЦП на всех сокетах.
Когда ЦП выполняет операцию, он сначала переходит в L1 для поиска необходимых данных, затем в L2, а затем в L3.Если в конце нет ни одного из этих кешей, необходимые данные отправятся в основную память, чтобы получить их . Чем дальше вы идете, тем больше времени занимает вычисление. Поэтому, если вы делаете что-то очень часто, вы стараетесь убедиться, что данные находятся в кеше L1.
Кроме того, когда часть данных совместно используется потоками, один поток требуется для записи данных обратно в основную память, в то время как другой поток обращается к соответствующим данным в основной памяти.
Вот понятие времени для доступа к различным уровням данных от ЦП:
Видно, что CPU считывает данные из оперативной памяти почти на 2 порядка медленнее, чем читает из L1.
строка кэша
Кэш состоит из множества строк кэша. Каждая строка кэша обычно имеет размер 64 байта и фактически относится к блоку адресов в основной памяти. Длина длинной переменной Java составляет 8 байт, поэтому в строке кэша может храниться 8 длинных переменных.
Каждый раз, когда ЦП извлекает данные из основной памяти, он сохраняет смежные данные в одной и той же строке кэша.
При доступе к массиву длинных значений, если одно значение в массиве загружается в кеш, оно автоматически загружает остальные 7. Таким образом, вы можете очень быстро перемещаться по массиву. На самом деле вы можете очень быстро просмотреть любую структуру данных, расположенную в смежных блоках памяти.
ложный обмен
Поскольку несколько потоков одновременно обрабатывают разные переменные одной и той же строки кэша, но между этими переменными нет никакой связи, но каждая модификация приведет к тому, что кэшированные данные станут недействительными, так что нет измененного содержимого, его все равно нужно удалить. в основную память Среднее чтение (процессор читает данные в основную память почти на 2 порядка медленнее, чем чтение из L1), но на самом деле это содержимое не меняется, т. к. наименьшая единица кэша — это кэш линия, которая является ложным разделением.
Если у вас есть переменные, которые часто обрабатываются несколькими потоками и не имеют отношения к разным строкам кеша, то модификация несвязанных переменных не повлияет на немодифицированные переменные для чтения основной памяти из-за проблемы строки кеша (тогда выборка из L1 на 2 порядка быстрее из основного доступа), тогда производительность будет Намного лучше .
Тестовый эффект с ложным разделением и без него
путь кода:GitHub.com/Цзян Синьлинь…
Использование заполнения байтов для устранения ложного обмена делает это примерно в 3 раза быстрее.
FastThreadLocal использует заполнение байтов для устранения ложного совместного использования
Когда я представил ThreadLocal ранее, я сказал, что ThreadLocal используется в многопоточных сценариях, а FastThreadLocal также используется в многопоточных сценариях, вы можете прочитать это:Разорвите вопросы интервью ThreadLocal! ! !, поэтому FastThreadLocal необходимо решить проблему ложного совместного использования, FastThreadLocal использует заполнение байтов для решения ложного совместного использования.
Это рассчитывается моей собственной рукой, слишком хлопотно вычислять вручную, я рекомендую инструментJOL.
откройте JDK.java.net/projects/co…
Рекомендуемые плагины IDEA:plugins.jet brains.com/plugin/1095…
путь кода:GitHub.com/Цзян Синьлинь…
С помощью этого инструмента очень легко рассчитать.Если в будущем возникнет подобная необходимость, не рассчитывайте один за другим вручную.
FastThreadLocal также может использовать строки кэша при чтении и записи с помощью FastThreadLocalThread.
И поскольку, когда поток является FastThreadLocalThread, операция FastThreadLocal заключается в сохранении данных через массив indexedVariables. Каждый FastThreadLocal имеет постоянный индекс, и индекс используется для прямого поиска массива для операций чтения и записи. Когда имеется много FastThreadLocals, вы также можете использовать строку кэша, например данные третьей позиции массива indexedVariables, поскольку наименьшая единица кэша — это строка кэша, кстати, следующие 4, 5, 6 и т. д. также кэшируются. , когда индекс FastThreadLocal равен 5, выполнить При чтении он идет напрямую в кеш, который может быть на 2 порядка быстрее, чем основная память.
небольшое сомнение
проблема:Почему здесь заполнено 9 длинных значений? ? ?
Я поднял вопрос:GitHub.com/Нетти/Нетти…
Хотя некоторые люди ответили, но я чувствую, что это не то, чего я хочу, я не могу себя убедить! ! !
FastThreadLocal быстрее, чем ThreadLocal, а не по времени.
Теперь очистка была удалена, о чем будет рассказано позже в этой статье, поэтому FastThreadLocal работает быстрее, чем ThreadLocal, а не пространство за время, FastThreadLocal не тратит место впустую! ! !
FastThreadLocal больше не использует ObjectCleaner для борьбы с утечками, при необходимости рекомендуется переопределить метод onRemoval.
ObjectCleaner больше не используется для устранения утечек в последней версии netty:
Устранить причину:
Давайте посмотрим на onRemoval FastThreadLocal.
Если вы используете FastThreadLocalThread, который может быть гарантированно вызван, перепишите onRemoval, чтобы внести некоторые изменения в конечное состояние и т. д.
Почему FastThreadLocal работает быстро?
Когда FastThreadLocal работает с элементами, он использует константные индексы для поиска элементов в массиве вместо ThreadLocal через хеширование и хеш-таблицы Это изменение особенно эффективно при частом использовании! Чтобы вычислить местоположение, в котором должен храниться ThreadLocal, нужно определить местоположение с помощью алгоритма хеширования: int i = key.threadLocalHashCode & (len-1), а FastThreadLocal — это постоянный индекс индекса, который будет иметь значение, если количество выполнений велико.
И FastThreadLocal использует характеристики строки кеша.FastThreadLocal хранит данные через массив indexedVariables.Если есть несколько FastThreadLocals, можно также использовать строку кеша, например третью позицию данных массива indexedVariables за раз, потому что наименьшая единица кэша - это строка кэша. , кстати, следующие 4, 5, 6 и т. д. тоже кэшируются. В следующий раз поток просто должен прочитать еще один FastThreadLocal. Когда индекс этого FastThreadLocal равен 5, переходить непосредственно в кеш при чтении.Переход в основную память может быть на 2 порядка быстрее, а ThreadLocal децентрализован за счет хеширования.
Если вы чувствуете себя вознагражденным после прочтения, пожалуйста, поставьте лайк, подпишитесь и добавьте официальную учетную запись [Ingenuity Zero], чтобы узнать больше захватывающей истории! ! !
)