1. Анализ небезопасного исходного кода класса
Небезопасенный класс в пакете JDK RT.JAR предоставляет атомные операции на уровне оборудования. Методы в небезопасных - все собственные методы, а локальные библиотеки реализации C ++ доступны с использованием JNI.
Объяснение основных функций небезопасного класса в Rt.Jar. Небезопасная класс обеспечивает атомные операции на уровне аппаратного уровня и может безопасно использовать переменные памяти напрямую. Это широко используется в исходном коде JUC. Понимание его принципов закладывает основу для изучения исходного кода JUC.
Во -первых, давайте поймем использование основных методов в небезопасном классе, следующим образом:
1. Способность ObjectFieldOffset (поле поля): возвращает адрес смещения памяти указанной переменной в классе, к которому она принадлежит. Адрес смещения используется только при доступе к указанному поле в небезопасной функции. В следующем коде используется небезопасно для получения смещения памяти переменной значения в атомиклонге в объекте Atomiclong. Код заключается в следующем:
static {try {valueOffset = uncafe.objectFieldoffset (atomiclong.class.getDeclaredfield ("value")); } catch (Exception ex) {бросить новую ошибку (ex); }}2. Метод INT ArrayBaseOffset (класс ArrayClass): Получите адрес первого элемента в массиве
3. Сделайте Arrayindexscale (класс Arrayclass): Получите количество байтов, занятых одним элементом в массиве
3. Boolean CompareAndsWaplong (объект obj, долгое смещение, долгое ожидание, длинное обновление): сравните, равносит ли значение переменной смещения смещения в объекте OBJ. Если это равно, он обновляется со значением обновления, а затем возвращает true, в противном случае он вернет false.
4. Публичный нативный Длинный GetLongvolative (Object obj, Long Offset) Метод: Получите значение семантики летучей памяти, соответствующей переменной смещения смещения в объекте obj.
5. Void PutOrderLong (объект obj, длинное смещение, длинное значение): установите значение длинного поля, соответствующего адресу смещения смещения в объекте OBJ, чтобы получить значение. Это метод Putlongvolatile с задержкой, и он не гарантирует, что изменение значения будет немедленно видна для других потоков. Переменные полезны только в том случае, если они изменены с помощью летучих и ожидается, что они будут неожиданно изменены.
6. Void Park (Boolean Isabsolute, долгое время) Метод: заблокировать текущую резьбу. Когда параметр iSabsolute равен ложному, время, равное 0, означает блокировку все время. Время больше 0 означает, что блокирующая нить будет пробуждена после ожидания указанного времени. Это время является относительным значением, постепенным значением, то есть текущим потоком будет пробужден после накопления по сравнению с текущим временем. Если Isabsolute равен True, а время больше 0, это означает, что он будет пробужден после блокировки в указанный момент времени. Здесь время это абсолютное время, которое является значением, преобразованным в MS в определенный момент времени. Кроме того, когда другие потоки вызывают метод прерывания текущего блокировки и прерывать текущий поток, текущий поток также вернется. Когда другие потоки вызывают метод Unpark и возьмут текущий поток в качестве параметра, текущий поток также вернется.
7. Способность void unpark (объект) Метод: Разбудите блокирующий резьбу после вызова парка, и параметры - это потоки, которые необходимо разбудить.
Несколько новых методов были добавлены в JDK1.8. Вот простой список методов работы длинного типа следующим образом:
8. МЕТОДА ДЕЙСТВИТЕЛЬНО ДЕТАНДЕТЛОНГ (объект obj, длительное смещение, длинное обновление): Получите значение переменной летучей семантики с смещением в объекте obj и установите значение переменной летучей семантики для обновления. Метод использования следующим образом:
public final long getandsetlong (объект obj, долгое смещение, длинное обновление) {long l; do {l = getlongvolatile (obj, offset); // (1)} while (! CompareandSwaplong (obj, offset, l, обновление)); возврат L; }Из внутреннего кода (1) вы можете использовать getLongvolation для получения значения текущей переменной, а затем использовать атомную операцию CAS для установки нового значения. Здесь использование, в то время как петли учитывают ситуацию, когда несколько потоков вызовывают одновременно, а затем требуется спин-резист после сбоя CAS.
9. Long GetandAddlong (Object obj, Long Offset, Long AddValue): Получите значение семантики переменной изменчивой с смещением в Object obj, и установите значение переменной на исходное значение + AddValue. Метод использования следующим образом:
public final long getandaddlong (объект obj, long offset, long addvalue) {long l; do {l = getlongvolatile (obj, offset); } while (! CompareandWaplong (obj, offset, l, l + addvalue)); возврат L; }Аналогично реализации GetAndSetLong, за исключением того, что при использовании CAS здесь используется исходное значение + значение передаваемого дополнительного параметра.
Так как использовать небезопасную класс?
Видеть, что это небезопасно, так здорово, вы действительно хотите практиковать? Хорошо, давайте сначала посмотрим на следующий код:
пакет com.hjc; import sun.misc.unsafe;/*** Создан Cong 2018/6/6. */public class testunsafe {// Получить экземпляр небезопасного (2.2.1) Статического окончательного конечного небезопасного = небезопасно.getunsafe (); // Записывают значение смещения переменного состояния в классе TestunSafe (2.2.2) Статический окончательный длинный AtteCoftSet; // переменная (2.2.3) Частное летучие длинное состояние = 0; static {try {// Получить значение смещения переменной состояния в классе TestunSafe (2.2.4) staceOffset = unceabe.objectfieldoffset (testunsafe.class.getDeclaredfield ("state")); } catch (Exception ex) {System.out.println (ex.getLocalizedMessage ()); бросить новую ошибку (Ex); }} public static void main (string [] args) {// Создать экземпляр и установить значение состояния на 1 (2.2.5) testunSafe test = new TestunSafe (); //(2. 2 System.out.println (успех); }}Код (2.2.1) получает экземпляр небезопасного, а код (2.2.3) создает состояние переменной, инициализированное до 0.
Code (2.2.4) использует unceabe.objectfieldoffset для получения адреса смещения памяти переменной состояния в классе TestunSafe в объекте TestunSafe и сохранение ее в переменной AtteAffSet.
Код (2.2.6) вызывает метод ComparandsWapint созданного небезопасного экземпляра и устанавливает значение переменной состояния тестового объекта. В частности, если переменная состояния, смещение памяти, смещение тестового объекта, составляет 0, составляет 0, значение обновления изменяется на 1.
Мы хотим ввести true в приведенный выше код, но после выполнения будут выведены следующие результаты:
Почему это происходит? Вы должны ввести код GetunSafe, например, посмотрите, что в нем сделано:
Частный статический финал небезопасно TheUnsafe = new Unfabe (); public static untabe getunsafe () {// (2.2.7) класс localclass = Reflection.getCallerclass (); // (2.2.8) if (! Vm.issystemdomainloader (localclass.getclassloader ())) {бросить новое безопасное Exception ("небезопасно"); } return theUnsafe;} // Судья, является ли параметров загрузчика ParamClassLoder (2.2.9) Public Static Boolean IssystemdomainLoader (ClassLoader ParamClassLoader) {return ParamClassLoader == null; }Код (2.2.7) Получает объект класса объекта, который вызывает getunsafe, здесь это testunsafe.cals.
Код (2.2.8) определяет, является ли это локальный класс, загруженный загрузчиком класса начальной загрузки. Ключ здесь заключается в том, загружает ли загрузчик Bootstrap TestunSafe.class. Те, кто видел механизм загрузки класса виртуальной машины Java, ясно видят, что это потому, что TestunSafe.class загружается с использованием загрузчика Appclass, поэтому здесь бросается исключение.
Итак, вопрос в том, зачем вам это суждение?
Мы знаем, что небезопасный класс предоставлен в Rt.Jar, а класс в Rt.Jar загружен с использованием загрузчика класса Bootstrap. Класс, в котором мы запускаем основную функцию, загружается с использованием загрузчика Appclass, поэтому при загрузке небезопасного класса в основной функции, учитывая, что родительский механизм делегирования будет делегировать для загрузки для загрузки небезопасного класса.
Если нет аутентификации кода (2.2.8), то наше приложение может использовать небезопасно, чтобы делать вещи по желанию. Небезопасный класс может управлять памятью напрямую, что очень небезопасно. Таким образом, команда разработчиков JDK специально сделала это ограничение, не позволяя разработчикам использовать небезопасную класс по обычным каналам, но использовать небезопасные функции в классе Core в Rt.Jar.
Вопрос в том, что мы должны делать, если мы действительно хотим создавать создание небезопасного класса и использовать небезопасную функцию?
Мы не должны забывать о черной технологии отражения и использовать универсальное отражение для получения метода экземпляра небезопасности. Код заключается в следующем:
пакет com.hjc; import sun.misc.unsafe; импорт java.lang.reflect.field;/*** Создан Конг на 2018/6/6. */public class testunsafe {static final небезопасно; Статический последний длинный Actaffest; частное летучие длинное состояние = 0; static {try {// отразить, чтобы получить переменную члена The Unsafe TheUnsafe (2.2.10) Field = небезопасно.class.getDeclaredfield ("theUnsafe"); // установить в доступный (2.2.11) Field.SetAccessible (true); // Получить значение этой переменной (2.2.12) небезопасно = (небезопасно) Field.get (null); // Получить смещение состояния в TestUnSafe (2.2.13) actaffset = uncefe.objectfieldoffset (testunsafe.class.getdeclaredfield ("state")); } catch (Exception ex) {System.out.println (ex.getLocalizedMessage ()); бросить новую ошибку (Ex); }} public static void main (string [] args) {testunSafe test = new TestunSafe (); Логический успех = небезопасно. System.out.println (успех); }}Если приведенный выше код (2.2.10 2.2.11 2.2.12) отражает пример небезопасного, результат выполнения следующего:
2. Исследование исходного кода класса LockSupport
LockSupport в Rt.Jar в JDK - это класс инструментов, и его основная функция - приостановить и разбудить потоки. Это основа для создания замков и других классов синхронизации.
Класс LockSupport будет связан с каждым потоком, который его использует. Поток, который вызывает метод класса LockSupport по умолчанию, не имеет лицензии. LockSupport реализуется внутри страны с использованием небезопасного класса.
Здесь мы должны обратить внимание на несколько важных функций локша, следующим образом:
1. Void Park () Метод: если вызовов Thread Calling Park () получил лицензию, связанную с LockSupport, то позвоните в LockSupport.park () немедленно вернется. В противном случае вызывающему потоку будет запрещено участвовать в планировании потока, то есть он будет заблокирован и приостановлен. Следующий пример - следующий код:
пакет com.hjc; import java.util.concurrent.locks.locksupport;/*** Создан Конг 2018/6/6. */public class locksupporttest {public static void main (string [] args) {System.out.println ("Park Start!"); Locksupport.park (); System.out.println ("Парк Стоп!"); }}Как показано в приведенном выше коде, непосредственно вызовите метод парка в основной функции, и конечный результат только вызовет старт парка! Затем текущий поток будет приостановлен, потому что вызовный поток не имеет лицензии по умолчанию. Результаты работы следующие:
Когда вы видите другие потоки, вызывая метод unpark (потока потока), а текущий поток используется в качестве параметра, поток, который вызывает метод парка, вернется. Кроме того, другие потоки вызывают метод Enterrupt () блокировки. Когда флаг прерывания установлен или блокирующий поток вернется после ложного пробуждения потока, лучше всего использовать условия цикла.
Следует отметить, что поток, который вызывает заблокированный метод Park (), прерывается другими потоками, а заблокированная резьба возвращает, не будет выбросить исключение прерванного эктри.
2. Способность void unpark (потока потока) Когда поток вызывает unpark, если поток потока параметра не содержит лицензию, связанную с потоком, и класс Locksupport, позвольте потоке удерживать его. Если нить, называемая Park (), приостановлен до того, как поток вызывается, поток будет пробуждена после вызова UNPARK.
Если нить не называлась Park ранее, после вызова метода Unpark, метод Park () будет возвращен немедленно. Приведенный выше код изменен следующим образом:
пакет com.hjc; import java.util.concurrent.locks.locksupport;/*** Создан Конг 2018/6/6. */public class locksupporttest {public static void main (string [] args) {System.out.println ("Park Start!"); // Сделать текущий поток получить лицензию locksupport.unpark (thread.currentthread ()); // позвонить в Park locksupport.park () снова; System.out.println ("Парк Стоп!"); }}Результаты работы следующие:
Далее, мы рассмотрим пример, чтобы углубить наше понимание парка, невозможное, код заключается в следующем:
Import java.util.concurrent.locks.locksupport;/*** Создан Конг на 2018/6/6. */public class locksupporttest {public static void main (string [] args) throws urruptexception {thread Think = new Thread (new Runnable () {@Override public void run () {System.out.println («starm Park thread Thread! }}); // запустить поток для дочернего потока. Start (); // Главная нить спит 1S Thread.sleep (1000); System.out.println («Основной поток unpark start!»); // Вызовы unpark, чтобы позволить потоке удерживать лицензию, а затем метод парка вернет locksupport.unpark (Thread); }}Результаты работы следующие:
Приведенный выше код сначала создает ветку дочернего потока. После запуска детская нить вызывает метод парка. Поскольку детская поток по умолчанию не имеет лицензии, он будет зависеть от себя.
Главная нить спит в течение 1 с. Цель состоит в том, что главный поток вызывает метод Unpark и позволяет ребенку выводить начнут парк -парк ребенка! и блок.
Основной поток затем выполняет метод unpark, причем параметр является дочерним потоком, цель состоит в том, чтобы позволить ребенку удерживать лицензию, а затем метод парка, вызванный дочерним потоком.
При возвращении метода парка он не скажет вам, по какой причине он возвращается. Следовательно, вызывающему нужно снова проверить, удовлетворено ли условием в зависимости от того, какой метод парка он был в текущем вызове. Если это не встречено, ему нужно снова вызвать метод парка.
Например, состояние прерывания потока при его возвращении, можно определить, возвращается ли оно, поскольку оно прерывается на основе сравнения состояния прерывания до и после вызова.
Чтобы проиллюстрировать, что поток после вызова метода парка вернется после того, как он будет прерван, измените приведенный выше пример кода и удалите locksupport.unpark (Thread); и затем добавить Thread.interrupt (); Код заключается в следующем:
Import java.util.concurrent.locks.locksupport;/*** Создан Конг на 2018/6/6. */public class locksupporttest {public static void main (string [] args) бросает прерывания {thread thread = new Thread (new Runnable () {@Override public void run () {System.out.println («Подгрупп -парк подбородок!») (! Thread.currentThread (). IsErenge ()) {locksupport.park (); // запустить Dealh Thread Thread.start (); // Основная резьба спит 1S Thread.sleep (1000); System.out.println («Основной поток unpark start!»); // прерывание потока ребенка. }}Результаты работы следующие:
Как приведенный выше код, дочерний поток заканчивается только после прерывания детского потока. Если детский поток не прерывается, даже если вы называете unpark (поток), дочерний поток не закончится.
3. Метод Void Parknanos (Long Nanos): Аналогично парку, если вызовный парк потока получил лицензию, связанную с Locksupport, то позвоните в LockSupport.park () немедленно вернется. Разница состоит в том, что если поток, вызывающий поток, не будет получен, он будет приостановлен, а затем возвращается после времени наноса.
Парк также поддерживает три метода с параметрами блокировщика. Когда нить вызывает парк, не имея лицензии и заблокирован и приостановлен, объект блокировщика будет записан внутри потока.
Используйте диагностические инструменты, чтобы наблюдать причину, по которой поток блокируется. Диагностические инструменты используют метод GetBlocker (потока) для получения объекта блокировщика. Таким образом, JDK рекомендует использовать метод парка с параметрами блокировщика и установить блокировщик на это, поэтому, когда ошибки памяти не устраняют проблему, мы можем знать, какой класс заблокирован.
Примеры следующие:
Import java.util.concurrent.locks.locksupport;/*** Создан Конг на 2018/6/6. */public class testpark {public void testpark () {locksupport.park (); // (1)} public static void main (string [] args) {testpark testpark = new TestPark (); testpark.testpark (); }}Результаты работы следующие:
Вы можете видеть, что он запускает блокировку, поэтому нам нужно использовать инструменты в каталоге JDK/BIN, чтобы посмотреть. Если вы не знаете, рекомендуется сначала взглянуть на инструменты мониторинга JVM.
При использовании JStack PID для просмотра стека потоков после запуска результаты, которые вы можете увидеть, заключаются в следующем:
Затем мы изменяем приведенный выше код (1) следующим образом:
Locksupport.park (это); // (1)
Запустите его снова и используйте JStack PID, чтобы просмотреть результаты следующим образом:
Можно видеть, что после метода парка с блокировщиком стек потоков может предоставить больше информации о блокировке объектов.
Затем мы проверим исходный код функции парка (блокировщик объектов), исходный код заключается в следующем:
public static void Park (блокировщик объектов) {// Получить поток вызова t = thread.currentthread (); // Установить переменную блокировку SetBlocker (t, Blocker); // Повесить нить небезопасно.park (false, 0l); // очистить переменную блокировщика после активации потока, потому что причина обычно анализируется, когда поток блокируется SetBlocker (t, NULL);}В резьбе есть переменная. Он используется для хранения объекта блокировщика, передаваемого парком, то есть переменная блокировщика хранится в переменной элемента потока, вызывающего метод парка.
4. Функция void parknanos (блокировщик объектов, длинные нано) имеет дополнительное время ожидания по сравнению с парком (блокировка объекта).
5. void parkuntil (блокировщик объектов, длительный крайний срок) Исходный код Parkuntil заключается в следующем:
public static void parkuntil (блокировщик объектов, длинный крайний срок) {Thread t = Thread.currentThread (); SetBlocker (t, блокировщик); // isabsolute = true, время = крайний срок; означает, что небезопасно. Парк (правда, крайний срок); setBlocker (t, null); }Вы можете видеть, что это установленный крайний срок, временная единица составляет миллисекунды, которая преобразуется в значение после миллисекундов с 1970 года в настоящий момент времени. Разница между этим и Parknanos (блокировщик объектов, длинные нано) состоит в том, что последний вычисляет время ожидания нанос с текущего времени, в то время как первый определяет момент времени.
Например, нам нужно подождать до 20:34 на 2018.06.06, а затем преобразовать этот момент времени в общее количество миллисекундов с 1970 года до того времени.
Давайте посмотрим на другой пример, код выглядит следующим образом:
Import java.util.queue; import java.util.concurrent.concurrentLinkedQueue; импорт java.util.concurrent.atomic.atomicboolean; импорт java.util.concurrent.locks.locksupport;/*** Создан Конгом 2018/6/6. */public class fifomutex {private final atomicboolean locked = new Atomicboolean (false); Приватная окончательная очередь <Thust> Officents = new CONGURRENTLINKEDQUEUE <THITE> (); public void lock () {boolean был мешан = false; Think Current = Thread.currentThread (); официанты.адд (текущий); // Только поток главы команды может получить блокировку (1) while (quieters.peek ()! = Current ||! Locked.compareandset (false, true)) {locksupport.park (this); if (Thread.Erenterted ()) // (2) был мешан = true; } официанты.remove (); if (был прерван) // (3) current.interrupt (); } public void unlock () {locked.set (false); Locksupport.unpark (официанты.peek ()); }}Вы можете видеть, что это первая в первую очередь блокировку, то есть только элемент заголовка очереди может получить его. Код (1) Если текущий поток не является заголовком очереди или текущая блокировка была получена другими потоками, то вызовите метод парка, чтобы приостановить себя.
Тогда Кодекс (2) выносит суждение. Если метод парка возвращается, потому что он прерывается, прерывание игнорируется, и флаг прерывания сброшен, и только флаг сделан, а затем снова определите, является ли текущий поток элементом головки очереди или заблокировался другими потоками. Если это так, продолжайте позвонить в метод парка, чтобы повесить себя.
Затем, если знак верно в коде (3), поток будет прерван. Как вы это понимаете? На самом деле, другие потоки прервали поток. Хотя меня не интересует сигнал прерывания и игнорировать его, это не означает, что другие потоки не заинтересованы в флаге, поэтому мне нужно его восстановить.
Суммировать
Вышеуказанное - все содержание этой статьи. Я надеюсь, что содержание этой статьи имеет определенную справочную ценность для каждого обучения или работы. Если у вас есть какие -либо вопросы, вы можете оставить сообщение для общения. Спасибо за поддержку Wulin.com.