В общем развитии автор часто видит, что многие студенты используют только некоторые основные методы в лечении модели одновременной разработки Java. Например, нестабильный, синхронизированный. Усовершенствованные параллельные пакеты, такие как Lock и Atomic, не часто используются многими людьми. Я думаю, что большинство причин связано с отсутствием атрибутов к принципу. В занятой разработке, кто может точно понять и использовать правильную модель параллелизма?
В последнее время, основываясь на этой идее, я планирую организовать механизм контроля параллелистика в статью. Это не только память о ваших собственных знаниях, но и надеется, что контент, упомянутый в этой статье, может помочь большинству разработчиков.
Разработка параллельной программы неизбежно включает в себя такие проблемы, как многопоточное сотрудничество и многозадачное сотрудничество и обмен данными. В JDK предоставляется несколько способов реализации параллельного управления между несколькими потоками. Например, обычно используется: внутренняя блокировка, блокировка повторного входа, блокировка чтения-записи и семафор.
Модель памяти Java
В Java каждый поток имеет область рабочей памяти, в которой хранится копия значения переменной в основной памяти, разделенной всеми потоками. Когда поток выполняется, он управляет этими переменными в своей собственной рабочей памяти.
Чтобы получить доступ к общей переменной, поток обычно получает блокировку и очищает область рабочей памяти, которая гарантирует, что общая переменная правильно загружена из области общей памяти всех потоков в область рабочей памяти потока. Когда поток разблокируется, значение переменной в области рабочей памяти гарантированно связано с общей памятью.
Когда поток использует определенную переменную, независимо от того, правильно ли программа использует операции синхронизации потока, значение, которое он получает, должно быть значением, хранящимся в переменной само по себе или другим потокам. Например, если два потока хранят разные значения или ссылки на объекты в одну и ту же общую переменную, то значение переменной происходит либо из этого потока, либо из этого потока, а значение общей переменной не будет состоит из эталонных значений двух потоков.
Адрес, к которому программы Java могут получить доступ при использовании переменной. Он включает не только переменные основного типа и переменные типа ссылки, но и переменные типа массива. Переменные, хранящиеся в основной области памяти, могут быть переданы всеми потоками, но один поток не может получить доступ к параметрам или локальным переменным другому потоку, поэтому разработчикам не нужно беспокоиться о проблемах безопасности потоков локальных переменных.
Волатильные переменные можно увидеть между несколькими потоками
Поскольку в каждом потоке есть своя область рабочей памяти, она может быть невидимой для других потоков, когда один поток меняет свои собственные данные рабочей памяти. Для этого вы можете использовать летучие ключевое слово, чтобы разбить все потоки для чтения и записи переменных в памяти, чтобы летучие переменные были видны среди нескольких потоков.
Переменные, объявленные как летучие, могут быть гарантированы следующим образом:
1. Модификации переменных другими потоками могут быть быстро отражены в текущем потоке;
2. Убедитесь, что модификация текущего потока изменчивой переменной может быть записана обратно в общую память во времени и видно из других потоков;
3. Используйте переменные, объявленные летучими, и компилятор обеспечит их упорядоченность.
Синхронизированные ключевые слова
Синхронизированный ключевой слов синхронизированный является одним из наиболее часто используемых методов синхронизации на языке Java. В ранних версиях JDK производительность Synchronized была не очень хорошей, а значение была подходящей для случаев, когда конкуренция за блокировки не была особенно жесткой. В JDK6 разрыв между синхронизированными и несправедливыми замками сузился. Что еще более важно, синхронизированный более краткий и ясный, а код читается и поддерживается.
Методы блокировки объекта:
Public Synchrinized void Method () {}
Когда метод Method () вызывается, вызовный поток должен сначала получить текущий объект. Если текущая блокировка объекта удерживается другими потоками, вызовный поток будет ждать. После того, как нарушение закончится, блокировка объекта будет выпущена. Приведенный выше метод эквивалентен следующему методу написания:
public void method () {synchronized (this) {// что -то делать…}} Во -вторых, синхронизированный также можно использовать для построения блоков синхронизации. По сравнению с методами синхронизации блоки синхронизации могут более точно контролировать диапазон кодов синхронизации. Небольшой код синхронизации очень быстрый в блокировке и за его пределами, что дает системе более высокую пропускную способность.
public void Метод (объект o) {// beforesynchronized (o) {// что -то ...} // после} Синхронизированный также может использоваться для статических функций:
Публичный синхронизированный static void method () {}
В этом месте важно отметить, что в текущий объект класса добавляется синхронизированный блокировка, поэтому все вызовы этого метода должны получить блокировку объекта класса.
Хотя синхронизированный может обеспечить безопасность потока объектов или сегментов кода, использование только синхронизации все еще недостаточно для управления взаимодействиями потока со сложной логикой. Чтобы достичь взаимодействия между несколькими потоками, также требуются методы wait () и notify () объекта объекта.
Типичное использование:
синхронизированный (obj) {while (<?>) {obj.wait (); // продолжать выполнять после получения уведомления. }} Перед использованием метода wait () вам необходимо получить блокировку объекта. Когда метод wait () выполняется, текущий поток может выпустить эксклюзивную блокировку OBJ для использования другими потоками.
При ожидании потока на OBJ получите obj.notify (), он может восстановить эксклюзивный замок OBJ и продолжать работать. Обратите внимание, что метод notify () состоит в том, чтобы случайным образом вызвать поток, ожидающий текущего объекта.
Вот реализация блокирующей очереди:
public Class Blockqueue {Private List = New ArrayList (); public Synchrinized Object pop () Throws urruptedException {while (list.size () == 0) {this.wait (); } if (list.size ()> 0) {return list.remove (0); } else {return null; }} открытый синхронизированный объект put (Object obj) {list.add (obj); this.notify (); }} Синхронизированный и wait () и notify () должен быть основным навыком, который должны освоить разработчики Java.
Reentrantlock Reentrantlock Lock
Reentrantlock называется повторным блоком. Он имеет более мощные особенности, чем синхронизированный, он может прерывать и время. В случае высокого параллелистика он имеет очевидные преимущества производительности по сравнению с синхронизацией.
Reentrantlock предоставляет как справедливые, так и несправедливые замки. Справедливая блокировка-это первый в первом месте блокировки, и не может быть вырезан справедливый замок. Конечно, с точки зрения производительности производительность несправедливых замков намного лучше. Следовательно, в отсутствие особых потребностей, несправедливые замки должны быть предпочтительны, но синхронизированная индустрия блокировки не является абсолютно справедливой. Reentrantlock может указать, является ли блокировка справедливой при строительстве.
При использовании блокировки повторного входа обязательно выпустите блокировку в конце программы. Как правило, код для выпуска блокировки должен быть записан наконец. В противном случае, если исключение программы произойдет, Loack никогда не будет выпущен. Синхронизированный блокировка автоматически выпускается JVM в конце.
Классическое использование заключается в следующем:
попробуйте {if (lock.trylock (5, timeUnit.seconds)) {// Если он был заблокирован, попробуйте ждать 5s, чтобы увидеть, можно ли получить блокировку. Если блокировка не может быть получена после 5S, верните false, чтобы продолжить выполнение // lock.lockentertible (); может ответить на событие прерывания try {// ray} наконец {lock.unlock (); }}} catch (прерывание Exception e) {e.printstackTrace (); // Когда текущий поток прерывается (прерывание), будет выброшено прерываниеReentrantlock предоставляет богатый разнообразие функций управления блокировкой и гибко применяет эти методы управления для повышения производительности применения. Тем не менее, не рекомендуется использовать здесь reentrantlock. Повторный Lock - это продвинутый инструмент разработки, предоставляемый в JDK.
ReadWritelock Читайте и пишите блокировку
Чтение и написание разделения - очень распространенная идея обработки данных. Это следует считать необходимой технологией в SQL. ReadWritelock-это блокировка разделения чтения-записи, предоставляемой в JDK5. Читать и записи блокировки разделения могут эффективно помочь снизить конкуренцию за блокировки для повышения производительности системы. Сценарии использования для разделения чтения и записи являются в основном, если в системе количество операций чтения намного больше, чем операции записи. Как использовать его следующим образом:
Private reenterTreadWritelock ReadWritelock = new ReenterTreadWriteLock (); Private Lock Readlock = readWritelock.readlock (); Private Lock writeLock = readWritelock.writelock (); public handleread () Throws urfutexception {try {readlock.lock (); Thread.sleep (1000); возвращаемое значение; } наконец {readlock.unlock (); }} public Object handleread () Throws urruptedException {try {writeLock.lock (); Thread.sleep (1000); возвращаемое значение; } наконец {writeLock.unlock (); }} Условие объекта
Объект ConditionD используется для координации сложного сотрудничества между несколькими потоками. В основном связаны с замками. Экземпляр условия, связанный с блокировкой, может быть сгенерирован с помощью метода NewCondition () в интерфейсе блокировки. Соотношение между объектом условий и блокировкой похожа на использование объекта двух функций. Wait (), object.notify () и синхронизированные ключевые слова.
Здесь вы можете извлечь исходный код ArrayBlockingQueue:
Общедоступный класс ArrayBlockquequeue Extrable -quepueue infrevlizats blockqueue, java.io.serializable {/** Основное блокировка, охраняющая все доступ*/final reentrantlock lock;/** Условие ожидания*/частное окончательное условие notempty;/** Условие ожидания*/private final conditud AllosalargumentException (); this.Items = новый объект [емкость]; lock = new Reentrantlock (Fair); notempty = lock.newCondition (); // генерировать условие natfull = lock.newCondition ();} public void put (e E) бросает прерванные окончательный reentrantlock lock = this.lock; lock.lockintertible (); попробуйте {while (count == item.length) natufull.await (); вставить (e); } наконец {lock.unlock (); }} private void insert (e x) {items [putindex] = x; putindex = inc (putindex); ++ подсчет; notempty.signal (); // уведомление} public e Take () Throws TreaRptenException {final Reentrantlock Lock = this.lock; lock.lockintertible (); попробуйте {while (count == 0) // Если очередь пуста notempty.await (); // Затем очередь потребителей должна ждать непустового возврата сигнала (); } наконец {lock.unlock (); }} private E Extract () {final Object [] items = this.items; E x = this. элементы [takeIndex] = null; takeindex = inc (takeindex); --считать; natfull.signal (); // Уведомление put (), что очередь потока имеет свободное пространство return x;} // Другой код} Semaphore Semaphore <Br /> Semaphore обеспечивает более мощный метод управления для многопоточного сотрудничества. Семфор - это расширение к замку. Будь то синхронизированный внутренний блокировка или повторный зал, один поток допускает доступ к ресурсу одновременно, в то время как в семафоре может указать, что несколько потоков получают доступ к ресурсу одновременно. Из конструктора мы видим:
Public Semaphore (int разрешает) {}
Публичный семафор (int разрешения, логическая ярмарка) {} // может указать, является ли это справедливым
Разрешения указывают книгу доступа для семафора, что означает, сколько лицензий может быть применено одновременно. Когда каждый поток применяется только на одну лицензию за раз, это эквивалентно указанию того, сколько потоков может получить доступ к определенному ресурсу одновременно. Вот основные методы использования:
public void accire () бросает прерванные эктриэлексии {} // Попробуйте получить разрешение на доступ. Если он недоступен, поток будет ждать, зная, что поток выпускает разрешение или текущий поток прерывается.
public void accireUninterbultable () {} // аналогично affire (), но не отвечает на прерывания.
Public Boolean TryAcquire () {} // Попробуйте получить его, верно, если успешно, в противном случае неверно. Этот метод не будет ждать и вернется немедленно.
Public Boolean TryAcquire (длительный тайм -аут, время Unit) бросает прерывания
public void Release () // используется для выпуска лицензии после завершения ресурса доступа на месте, чтобы другие потоки, ожидающие разрешения, могли получить доступ к ресурсу.
Давайте посмотрим на примеры использования семафоров, представленных в документе JDK. Этот пример объясняет, как контролировать доступ к ресурсам через семафоры.
Public Class Pool {Private Static Final int max_available = 100; Приватный окончательный семафор доступен = новый семафор (max_available, true); public Object getItem () бросает прерывания. // Подать заявку на лицензию // Только 100 потоков могут входить для получения доступных элементов одновременно, // если более 100, вам нужно ждать возврата getNextAvailableTem ();} public void putitem (Object x) {// Поместить заданный элемент обратно в пул и отметьте его, как не используется, если (маркасунс (x)) {recavity.Release (); // Добавлен доступный элемент, выпустить лицензию и активирован поток, запрашивающий ресурс}} // Например, только ссылка, нереальные данные, защищенные данные [] items = new объект [max_available]; // Используется для объектов мультиплексирования пула объектов, защищенных Boolean [] ИСПОЛЬЗОВАНИЕ = NEW BOOLEAN [MAX_AVAILABLE]; // Функция разметки защищенного синхронизированного объекта getNextAvailAbleTem () {for (int i = 0; i <max_available; ++ i) {if (! Используется [i]) {используется [i] = true; вернуть предметы [i]; }} return null;} защищенный синхронизированный логический маркасунсин (Object Item) {for (int i = 0; i <max_available; ++ i) {if (item == elects [i]) {if (i]) {используется [i] = false; вернуть истину; } else {return false; }}} вернуть false;}} Этот экземпляр просто реализует пул объектов с максимальной емкостью 100. Поэтому, когда есть 100 запросов объектов в то же время, пул объектов будет иметь нехватку ресурсов, а потоки, которые не могут получить ресурсы, необходимо ждать. Когда поток заканчивается с помощью объекта, он должен вернуть объект в пул объектов. В настоящее время, поскольку доступные ресурсы увеличиваются, можно активировать поток, ожидающий ресурса.
Threadlocal Local переменные <br /> После того, как я только что начал связываться с Threadlocal, мне трудно понять сценарии использования этой локальной переменной потока. Когда оглядывается назад, Threadlocal - это решение для одновременного доступа к переменным между несколькими потоками. В отличие от синхронизированных и других методов блокировки, Threadlocal вообще не обеспечивает блокировки, но использует метод обмена пространством для времени, чтобы обеспечить каждому потоку независимые копии переменных для обеспечения безопасности потока. Следовательно, это не решение для обмена данными.
Threadlocal - хорошая идея для решения проблем безопасности потока. В классе Threadlocal есть карта, на которой хранится копия переменных для каждого потока. Ключом элемента в карте является объект потока, и значение соответствует копии переменных для потока. Поскольку значение ключа не может быть повторено, каждый «объект потока» соответствует «копии переменных» потока, и он достигает безопасности потока.
Это особенно примечательно. С точки зрения производительности, Threadlocal не имеет абсолютной производительности. Когда объем параллелизма не очень высок, производительность блокировки будет лучше. Однако в качестве набора защитных решений, которые полностью не связаны с замками, использование Threadlocal может в определенной степени снизить конкуренцию за блокировки в высокой параллельной или жесткой конкуренции.
Вот простое использование Threadlocal:
Открытый класс Testnum {// перезаписать метод Threadlocal's initialValue () через анонимный внутренний класс, укажите начальное значение Private Static Threadlocal seqnum = new Threadlocal () {public Integer initialValue () {return 0; }}; // Получить значение следующей последовательности public int getNextnum () {seqnum.set (seqnum.get () + 1); return seqnum.get ();} public static void main (string [] args) {testnum sn = new testnum (); // 3 потоки используют SN, каждый из которых генерирует номера последовательности TestClient T1 = New TestClient (SN); TestClient T2 = New TestClient (SN); TestClient T3 = New TestClient (SN); t1.start (); t2.start (); t3.start (); } private Static Class TestClient Extends Thread {Private Testnum sn; public TestClient (testnum sn) {this.sn = sn; } public void run () {for (int i = 0; i <3; i ++) {// Каждый поток создает 3 значения последовательности System.out.println ("Thread [" + thread.currentThread (). getName () + "] -> sn [" + sn.getNextnum () + "]"); }}}} Результат вывода:
Thread [Thread-0]> sn [1]
Thread [Thread-1]> SN [1]
Поток [Thread-2]> sn [1]
Thread [Thread-1]> SN [2]
Thread [Thread-0]> SN [2]
Thread [Thread-1]> SN [3]
Поток [Thread-2]> sn [2]
Thread [Thread-0]> SN [3]
Thread [Thread-2]> sn [3]
Информация о результате вывода может быть обнаружена, что, хотя номера последовательностей, сгенерированные каждым потоком, имеют один и тот же экземпляр Testnum, они не мешают друг другу, но каждый генерирует независимые номера последовательности. Это потому, что Threadlocal предоставляет отдельную копию для каждого потока.
Производительность блокировки и оптимизация «замков» являются одним из наиболее часто используемых методов синхронизации. В нормальной разработке вы часто можете видеть, как многие студенты напрямую добавляют большой кусок кода в блокировку. Некоторые студенты могут использовать только один метод блокировки для решения всех проблем обмена. Очевидно, что такое кодирование недопустимо. Особенно в средах с высоким содержанием параллелистика, жесткая конкуренция за блокировки приведет к более очевидной деградации производительности программы. Следовательно, рациональное использование замков напрямую связано с производительностью программы.
1. Накладные расходы потока <br /> В случае многоядерного использования с использованием мультипотчика может значительно улучшить производительность системы. Однако в реальных ситуациях использование мультипотчика добавит дополнительные системы системы. В дополнение к потреблению ресурсов самих системных задач, многопоточные приложения также должны поддерживать дополнительную многопоточную уникальную информацию. Например, метаданные самого потока, планирование потоков, переключение контекста потока и т. Д.
2. Сократите время удержания блокировки
В программах, которые используют блокировки для одновременного управления, когда соревнуются блокировки, время удержания блокировки одного потока имеет прямую связь с производительностью системы. Если поток долго держит замок, конкуренция за замк будет более интенсивной. Следовательно, в процессе разработки программы время для занимать определенное блокировку должно быть сведено к минимуму, чтобы уменьшить возможность взаимного исключения между потоками. Например, следующий код:
Публичный синхронизированный void syncmehod () {beaforemethod (); mutexmethod (); phesemethod ();} Если только метод mutexmethod () в этом случае является синхронным, но в beforemethod () и effemethod () не требуют контроля синхронизации. Если BeForeMethod () и effemethod () являются тяжелыми методами, это займет много времени для процессора. В настоящее время, если параллелизм велик, использование этой схемы синхронизации приведет к значительному увеличению потоков ожидания. Потому что в настоящее время выполняющий поток выпустит блокировку только после того, как все задачи были выполнены.
Ниже приведено оптимизированное решение, которое только синхронизируется только при необходимости, так что время для хранения потоков может быть значительно снижено, а пропускная способность системы может быть улучшена. Код заключается в следующем:
public void syncmehod () {beforemethod (); synchronized (this) {mutexmethod ();} effemethod ();} 3. Уменьшите размер блокировки частиц
Уменьшение гранулярности замка также является эффективным средством ослабления конкуренции за многопоточные замки. Типичным сценарием использования этой технологии является класс concurrenthashmap. В обычной Hashmap, когда выполняется операция add () или get () на коллекции, всегда получается блокировка объекта сбора. Эта операция является полностью синхронным поведением, потому что блокировка находится на всем объекте сбора. Следовательно, в высокой параллелистике жесткая конкуренция за блокировки повлияет на пропускную способность системы.
Если вы прочитали исходный код, вы должны знать, что HashMap реализован в списке массива + связанного состава. Concurrenthashmap делит всю Hashmap на несколько сегментов (сегментов), и каждый сегмент является подразделением. Если вам нужно добавить новую запись таблицы, вы не блокируете HashMap. Двадцать поисковых строк получит раздел, в котором запись таблицы должна храниться в соответствии с хэшкодом, а затем заблокировать раздел и завершить операцию put (). Таким образом, в многопоточной среде, если несколько потоков выполняют операции записи одновременно, если записанный элемент не существует в одном и том же сегменте, истинный параллелизм может быть достигнут между потоками. Для конкретной реализации я надеюсь, что читатели понадобится некоторое время, чтобы прочитать исходный код класса Concurrenthashmap, поэтому я не буду описать его слишком много здесь.
4. Разделение блокировки <br /> readwritelock читайте и записывает блокировку, упомянутая ранее, затем расширение разделения чтения и записи - это разделение блокировки. Исходный код разделения блокировки также можно найти в JDK.
Общедоступный класс LinkedBlockqueue Extends AbstractQueue Implames BlockingQueue, java.io.serializable {/*Lock Hold By Take, опрос и т. Д./Частный окончательный результат reenterlock takelock = new Reentrantlock ();/** a Wait queue для ожидания*/Private Final Condity notempty = takelock.newCondition (); Reentrantlock putlock = new Reentrantlock ();/** a ждать очередь ожидания, статус*/частное окончательное условие natfull = putlock.newcondition (); public e take () Throws retruptedException {ex; int c = -1; окончательный atomicinteger count = this.count; окончательный reentrantlock takelock = this.takelock; takelock.lockEntertaill (); // Не может быть двух потоков для чтения данных одновременно, try {while (count.get () == 0) {// Если нет доступных данных, подождите, пока уведомление put () notempty.await (); } x = dequeue (); // Удалить элемент c = count.getandDecrement (); // размер минус 1 if (c> 1) notempty.signal (); // Уведомление о других операциях ()} наконец {takelock.unlock (); // Отпустить блокировку} if (c == емкость) signalnotfull (); // Уведомление о операции put (), уже существует возврат свободного пространства x;} public void put (e) бросает прерывания. // Примечание: соглашение во всех Put/atmber/etc предназначен для предварительного расположения локального VAR // отрицательное количество отрицательного количества, чтобы указать сбой, если не установлено. int c = -1; Узел <e> node = new Node (e); Окончательный reentrantlock putlock = this.putlock; окончательный atomicinteger count = this.count; putlock.lockEntertaill (); // Не может быть двух потоков, чтобы помещать данные одновременно, попробуйте { / * * Обратите внимание, что счет используется в газоде, даже если он не защищен блокировкой. Это работает, потому что счет может * только уменьшаться на этом этапе (все остальные путы выключены * замок), и мы (или какой -то другой пут ожидания) * подписаны, если он когда -либо изменяется от емкости. Точно так же * для всех других видов использования подсчета в других охранниках. */ while (count.get () == емкость) {// Если очередь полна, подождите natfull.await (); } enqueue (node); // Присоединяйтесь к очереди c = count.getAndincrement (); // размер плюс 1 if (c + 1 <емкость) natfull.signal (); // Уведомление о других потоках, если пространство достаточно}, наконец, {putlock.unlock (); // Отпустите блокировку} if (c == 0) signalnotempty (); // после успешной вставки, уведомить операцию Take () для чтения данных} // Другой код}}}}}}}Здесь нужно объяснить, что функции Take () и put () не зависят друг от друга, и между ними нет никаких отношений соревнований. Вам нужно только конкурировать за Takelock и Putlock в соответствии с соответствующими методами take () и put (). Таким образом, возможность соревнования блокировки ослаблена.
5. Заблокировать грубость <br /> вышеупомянутое сокращение времени блокировки и детализации выполняется для того, чтобы соответствовать кратчайшему времени для каждой потока для удержания блокировки. Тем не менее, степень должна быть развлечена в гранулярности. Если блокировка постоянно запрашивается, синхронизируется и выпускается, он будет потреблять ценные ресурсы системы и увеличить накладные расходы системы.
Что нам нужно знать, так это то, что когда виртуальная машина встречает серию непрерывных запросов и выпусков одной и той же блокировки, она будет интегрировать все операции блокировки в один запрос на блокировку, тем самым уменьшая количество запросов на блокировку. Эта операция называется замкнутой грубостью. Вот демонстрация примера интеграции:
public void syncmehod () {synchronized (lock) {method1 ();} synchronized (lock) {method2 ();}} форма после интеграции jvm: public void syncmehod () {synchronized (lock) {method1 (); method2 ();}}}}Следовательно, такая интеграция дает нашим разработчикам хороший демонстрационный эффект на понимание гранулярности блокировки.
Бесплавные параллельные вычисления <br /> Вышеуказанное потратило много времени на разговоры о блокировке, и также упоминается, что блокировка принесет дополнительные накладные расходы ресурсов для определенного переключения контекста. В высокой параллеле жесткая конкуренция за «блокировку» может стать узким местом системы. Следовательно, здесь можно использовать метод неблокирующей синхронизации. Этот метод без блокировки все еще может гарантировать, что данные и программы сохраняют согласованность между несколькими потоками в среде с высоким содержанием параллелистики.
1. Не блокирующая синхронизация/беззаконая
Метод неблокирующей синхронизации фактически отражается в предыдущем потоке. Каждый поток имеет свою собственную независимую копию переменных, поэтому нет необходимости ждать друг друга при вычислении параллельно. Здесь автор в основном рекомендует более важный метод управления параллелизмом без блокировки, основанный на алгоритме сравнения и обмена CAS.
Процесс алгоритма CAS: он содержит 3 параметра CAS (V, E, N). V представляет собой переменную, которая должна быть обновлена, E представляет ожидаемое значение, а N представляет новое значение. Значение v будет установлено только на n только тогда, когда значение v равно значению E. Если значение V отличается от значения E, это означает, что другие потоки сделали обновления, а текущий поток ничего не делает. Наконец, CAS возвращает истинное значение текущего V. При использовании CAS он выполняется с оптимистичным отношением, и всегда считает, что он может успешно завершить операцию. Когда несколько потоков используют CAS для эксплуатации переменной одновременно, только один выиграет и будет успешно обновляться, в то время как остальная часть Junhui не проходит. Неудачный поток не будет приостановлен, только сказано, что сбой разрешена, и ему разрешено повторить попытку, и, конечно, неудачный поток также позволит отказаться от операции. Основываясь на этом принципе, операция CAS является своевременной без замков, а другие потоки также могут обнаружить помехи в текущий поток и обрабатывать его соответствующим образом.
2. Работа атомного веса
JDK Java.Util.concurrent.atomic Package предоставляет классы атомной работы, реализованные с использованием алгоритмов без блокировки, и в основном используется базовая реализация собственного кода. Заинтересованные студенты могут продолжать отслеживать код нативного уровня. Я не буду публиковать здесь реализацию кода поверхности.
В следующем в основном используется пример, чтобы показать разрыв в производительности между обычными методами синхронизации и без блокировки синхронизации:
Общедоступный класс Testatomic {Private Static Final int max_threads = 3; частная статическая окончательная конечная задача = 3; частная статическая конечная конечная конечная конечная int_count = 100 * 10000; private atomicinteger account = new atomicinteger (0); private int count = 0; синхронизированный int inc () {return ++ countecres Runnable {String name; долгое начало; Testatomic out; public syncthread (testatomic o, long Starttime) {this.out = o; this.starttime = starttime; } @Override public void run () {int v = out.inc (); while (v <target_count) {v = out.inc (); } long EndTime = System.CurrentTimeMiLlis (); System.out.println ("syncthread rants:" + (endtime - starttime) + "ms" + ", v =" + v); }} открытый класс AtomicThread реализует Runnable {String name; долгое начало; public atomicthread (long starttime) {this.starttime = startTime; } @Override public void run () {int v = account.incrementAndget (); while (v <target_count) {v = account.incrementAndget (); } long EndTime = System.CurrentTimeMiLlis (); System.out.println ("atomicthread rants:" + (endtime - starttime) + "ms" + ", v =" + v); }}@Testpublic void testsync () бросает прерывание {receectorservice exe = executors.newfixedthreadpool (max_threads); long starttime = System.currentTimeMillis (); Syncthread Sync = new Syncthread (This, StartTime); for (int i = 0; i <task_count; i ++) {exe.submit (sync); } Thread.sleep (10000);}@testpublic void testatomic () бросает прерывания. long starttime = System.currentTimeMillis (); AtomicThread Atomic = New Atomicththread (Starttime); for (int i = 0; i <task_count; i ++) {exe.submit (atomic); } Thread.sleep (10000);}} Результаты теста следующие:
testSync ():
Syncthread rants: 201ms, V = 1000002
Syncthread rants: 201ms, V = 1000000
Syncthread rants: 201ms, V = 1000001
testatomic ():
AtomicThread Entry: 43 мс, V = 1000000
AtomicThread Entry: 44 мс, V = 1000001
AtomicThread Entry: 46 мс, V = 1000002
Я полагаю, что такие результаты теста будут четко отражать различия в производительности между внутренними блокирующими и не блокирующими алгоритмами синхронизации. Поэтому автор рекомендует непосредственно посчитать этот атомный класс под атомным.
Заключение
Наконец, я разобрался с тем, что хочу выразить. На самом деле, есть еще несколько классов, таких как Countdownlatch, которые не были упомянуты. Однако то, что упоминается выше, определенно является ядром параллельного программирования. Возможно, некоторые читатели могут увидеть много таких знаний в Интернете, но я все еще думаю, что только по сравнению можно найти знания в подходящем сценарии. Поэтому это также является причиной, по которой редактор составил эту статью, и я надеюсь, что эта статья может помочь большему количеству студентов.
Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.