Недавно я прочитал код слоя Android Framework и увидел класс Threadlocal. Я был немного незнаком, поэтому я читал различные связанные блоги один за другим. Затем я изучил исходный код и обнаружил, что мое понимание отличалось от постов в блоге, которые я читал ранее, поэтому я решил написать статью, чтобы поговорить о моем понимании, надеясь, что она сможет сыграть следующую роль:
- может очистить результаты исследования и углубить ваше понимание;
- Это может сыграть роль в привлечении нефрита и помочь заинтересованным студентам очистить свои идеи;
- Поделитесь своим опытом обучения, общайтесь и учитесь со всеми.
1. Что такое нитолокальный
Threadlocal является основным классом библиотеки класса Java, под пакетом Java.lang;
Официальное объяснение заключается в следующем:
Реализует резьбу-локальное хранилище, то есть переменную, для которой каждый поток имеет свое собственное значение. Все потоки совместно используют один и тот же нитолокальный объект, но каждый видит различное значение при доступе к нему, а изменения, внесенные одним потоком, не влияют на другие потоки. Реализация поддерживает нулевые значения.
Общий смысл:
Местный механизм хранения потоков может быть реализован. Вернокальная переменная - это переменная, которая может иметь разные значения в разных потоках. Все потоки могут совместно использовать один и тот же резервный объект, но разные потоки могут получать различные значения при обращении, и изменения любого потока в него не будут влиять на другие потоки. Реализации класса поддерживают нулевые значения (нулевые значения могут быть переданы и доступны в методах набора и получения).
Таким образом, есть три характеристики:
- Получите разные значения при обращении к разным потокам
- Любое изменение потока в него не повлияет на другие потоки
- Поддержка NULL
Ниже приведены примеры этих функций. Сначала мы определяем тестовый класс. В этом классе мы проверяем три функции, упомянутые выше. Определение класса выглядит следующим образом:
Тест. Ява
Общедоступный тест класса {// определить Threadlocal Private Static Threadlocal Name; Public Static void Main (String [] args) Throws Exception {name = new Threadlocal (); // Определить Thread atHread a = new Thread () {public void run () {System.out.println («Перед вызовом set, значение -:»+name.get (); A "); System.out.println (" после invoke set, значение: "+name.get ());}}; // определить поток bthread b = new Thread () {public void run () {System.out.println (" перед набором vOuk, значение: "+name.get (); : "+name.get ());}}; // не вызывать набор, распечатать значение - nullsystem.out.println (name.get ()); // invoke set, чтобы заполнить ValueName.set (" main thread »); // Начало потока aa.start (); a.join (); // print after aftemporte the value. Thread Bb.Start (); B.Join (); // Распечатать значение после изменения значения по потоке bSystem.out.println (name.get ())}}Анализ кода:
Из определения мы видим, что объявляется только один Threadlocal объект, а другие три потока (основной поток, поток A и поток B) имеют один и тот же объект; Затем значение объекта изменяется в разных потоках, и значение объекта доступно в разных потоках, и результат выводится для просмотра в консоли.
Смотрите результаты:
Из результатов выходных консолей вы можете видеть, что в нем есть три нулевых выхода. Это связано с тем, что объект не был назначен до вывода, что подтвердило функцию поддержки NULL. Кроме того, можно обнаружить, что в каждом потоке я изменял значение объекта, но когда другие потоки получают доступ к объекту, это не модифицированное значение, а значение-локальное значение; Это также проверяет две другие функции.
2. Роль нитолокального
Все знают, что его сценарии использования в основном многопоточные программы. Что касается его конкретной функции, как сказать это? Я думаю, что это можно определить только в общем виде, потому что функциональные атрибуты вещи ограничат мышление каждого после того, как это будет определено. Например, кухонные ножи используются для разрезания овощей, и многие люди не будут использовать их для разреза арбузов.
Здесь я расскажу о своем понимании его функции только для справки и надеюсь, что это будет полезно. Давайте опишем это таким образом. Когда многопоточная программа должна инкапсулировать некоторые задачи большинства потоков (то есть, некоторый код в методе выполнения), Threadlocal можно использовать для обертывания связанных с потоком переменных элементов в корпусе инкапсуляции, чтобы обеспечить исключительность доступа к потоку, и все потоки могут обмениваться объектом инкапсуляции; Вы можете обратиться к Looper в Android. Программисты, которые не могут описать проблемы с кодом, не являются хорошими программистами;
Посмотрите на код: инструмент для указания многократного кода потока (созданный для иллюстрации проблемы)
Statisticcosttime.java
// класс, который статистика. Стоимость временного времени класса Statisticcosttime {// reporm the starttime // private threadlocal startTime = new Threadlocal (); Private Long StartTime; // private Threadlocal rovtime = new Threadlocal (); Private Long Longttime; private Statisticcosttime () {} // singletonpublic static final statistrictime () {{} // singletonpublic static statistriccosttime () {} // singletonpublic staticcosttime (). Exancefactory.instance;} частный статический класс exancefactory {private Static Linate statisticcosttime ancement = new Statisticcosttime ();} // startpublic void start () {// starttime.set (System. Nanotime ()); startTime = System.Nanotime ();} // endPublic void end () {//Sttime.Setme.Setime.SemeMeme () naneMeme.SemeMeme. startTime.get ()); opltime = system.nanotime () - startTime;} public long getStartTime () {return startTime; // return startTime.get ();} public long getCostTime () {// return rovtime.get (); return rovtime;}Хорошо, дизайн инструмента завершен, теперь мы используем его для подсчета трудоемких потоков и попытаться:
Main.java
открытый класс Main {public static void main (string [] args) выбрасывает Exception {// определить поток athread a = new Thread () {public void run () {try {// start record timeStatistictime.shareinstance (). start (); Sleep (200); // Печать время начала работы System.out.println ("a-starttime:"+statisticcosttime.shareinstance (). GetStarttime ()); // End a RecordStatisticcosttime.shareInstance (). End (); // Печать затраты на систему. e) {}}}; // запустить aa.start (); // определить поток bthread b = new Thread () {public void run () {try {// Записать время начала b1statisticcosttime.shareinstance (). start (); Sleep (100); // consosoosystem.out.println ("b1-starttime:"+statisticcosttime.shareinstance (). getStartTime ()); // Конец времени начала записи b1statisticcosttime.shareinstance (). End (); // B1system.out.println ("b1:"+statisticcosttime.shareinstance (). GetCosttime ()); // Запуск времени записи b2statisticcosttime.shareinstance (). Start (); Sleep (100); // Время начала печати работы B2system.out.println ("b2-starttime:"+statisticcosttime.shareinstance (). GetStartTime ()); // Запись времени записи b2statisticcosttime.shareinstance (). B2system.out.println ("b2:"+statisticcosttime.shareinstance (). Getcosttime ());} catch (исключение e) {}}}; b.start ();}}После запуска кода результат вывода выглядит следующим образом: точность результата выходного вывода - наносекунд.
Это зависит от того, отличается ли результат от того, что мы ожидали. Я обнаружил, что результат А должен быть приблизительно равен B1+B2. Почему это становится таким же, как B2? Ответ заключается в том, что когда мы определяем начальные переменные и переменные времени стоимости, исходное намерение не должно быть обменом, но должно быть исключительно для потока. Здесь переменные разделяются с Синглтоном, поэтому при расчете значения A начало началости фактически было изменено B2, поэтому вывод B2.
Теперь давайте откроем закомментированную часть в Statisticcosttime и попробуем ее, изменив ее на метод объявления Threadlocal.
Смотрите результаты:
Ах! Это достигло ожидаемого эффекта. В настоящее время некоторые студенты сказали бы, что это не доступ к потоке, и могу ли я обеспечить безопасность потока, если я использую Threadlocal? Ответ нет! Прежде всего, мы можем выяснить, почему есть проблемы с безопасностью нити, но есть только две ситуации:
1. У вас есть общие ресурсы, которые не должны делиться между потоками;
2. Вы не гарантируете упорядоченный доступ к ресурсам, разделяемым между потоками;
Первый может быть решен с помощью метода «пространственное время обмена», используя Threadlocal (вы также можете напрямую объявить локальные переменные потока), а последнее может быть решено с помощью метода «пространственное время обмена», что, очевидно, не может сделать Threadlocal.
3. Трендокальный принцип
Принцип реализации на самом деле очень прост. Каждый раз, когда операция чтения и записи в Threadlocal объект на самом деле является операцией чтения и записи для объекта значений потока; Здесь мы уточняем, что нет создания копии переменной, поскольку пространство памяти, выделяемое переменной, не используется для хранения объекта T, но значения потока используются для хранения T -объекта; Каждый раз, когда мы называем метод Threadlocal Set в потоке, это на самом деле процесс написания объекта в соответствующий объект потока потока; При вызове метода Threadlocal GET, это на самом деле процесс получения объекта из соответствующего объекта значений потока.
Смотрите исходный код:
Набор переменных с потоковыми элементами
/*** Устанавливает значение этой переменной для текущего потока. Если установить на * {@code null}, значение будет установлено в NULL, а базовая запись будет * все еще присутствовать. * * @param value Новое значение переменной для поток вызывающего абонента. */public void set (t value) {thread currentThread = thread.currentThread (); Значения значения = значения (currentThread); if (values == null) {values = initiazizeValues (currentThread); } values.put (this, value);}Метод члена Teathlocal Get
/*** Возвращает значение этой переменной для текущего потока. Если запись * еще не существует для этой переменной в этом потоке, этот метод * создаст запись, заполняя значение результатом * {@link #initialValue ()}. * * @return Текущее значение переменной для вызова потока. */@Spistresswarnings ("unchecked") public t get () {// оптимизирован для быстрого пути. Think CurrentThread = Thread.CurrentThread (); Значения значения = значения (currentThread); if (values! = null) {object [] table = values.table; int index = hash & values.mask; if (this.reference == таблица [index]) {return (t) таблица [index + 1]; }} else {values = initiazizeValues (currentThread); } return (t) values.getaftermiss (this);}Метод Threadlocal Method Инициализация Values
/*** Создает экземпляр значений для этого потока и типа переменной. */Values initiazizevalues (think current) {return current.localvalues = new Values ();}Значения метода Threadlocal Method
/*** Получает экземпляр значений для этого потока и типа переменной. */Values values (потока ток) {return current.localvalues;}Итак, как вы читаете и пишете объекты в этих значениях?
Значения существуют как внутренний класс Threadlocal; Эти значения включают в себя важный объект массива [], который является ключевой частью ответа на вопрос. Он используется для хранения различных типов беговых переменных в потоке. Итак, вопрос в том, как вы гарантируете, что не получаете значения других типов при принятии переменных определенного типа? В общем, карта будет нанесена на карту в соответствии с ключевой стоимостью; Да, идея заключается в этой идее, но она не реализована с картой здесь, это механизм карты, реализованный с объектом []; Однако, если вы хотите использовать карту, чтобы понять ее, это невозможно, потому что механизм такой же; Ключ на самом деле соответствует слабой ссылке на Threadlocal, и значение соответствует объекту, в котором мы передали.
Давайте объясним, как использовать объект [] для реализации механизма карты (см. Рисунок 1); Он использует паритет подписания массива, чтобы отличить ключ и значение, то есть в таблице ниже хранится ключ в равномерной позиции, а нечетное число сохраняет значение. Вот как это делается. Если заинтересованные студенты хотят узнать об реализации алгоритма, они могут изучить ее подробно, я не буду подробно объяснять это здесь.
Основываясь на предыдущем первом примере, ситуация хранения анализируется:
Когда программа выполнена, есть три потока A, B и Main. Когда name.set () вызывается в потоке, три идентичных пробела памяти выделяются в области кучи для трех экземпляров потока одновременно для хранения объекта значений, используя ссылку на имя в качестве ключа, а конкретный объект сохраняется в виде значения в трех разных объектах [] (см. Рисунок ниже):
4. Резюме
Threadlocal не может полностью решить проблему параллелизма во время многопоточного программирования. Эта проблема также требует различных решений для выбора в соответствии с различными ситуациями, «пространство для времени» или «время для пространства».
Самая большая функция Threadlocal заключается в преобразовании переменных, продемонстрированных потоком в ниточные переменные для достижения выделения между потоками.
Вышеупомянутое все о быстрое понимание нитолокальных в Java. Я надеюсь, что это будет полезно для всех. Если есть какие -либо недостатки, пожалуйста, оставьте сообщение, чтобы указать это. Спасибо, друзья, за вашу поддержку на этом сайте.