Синхронизированное ключевое слово
Синхронизированные, мы называем блокировки, в основном используются для блокировки методов и блоков кода. Когда метод или кодовый блок синхронизируется, не более одного потока выполняет код одновременно. Когда несколько потоков получают доступ к методу блокировки/кодовому блоку одного и того же объекта, одновременно выполняется только один поток, а оставшиеся потоки должны ждать, пока текущий поток выполнит сегмент кода перед выполнением. Тем не менее, оставшиеся потоки могут получить доступ к блокам не заблокированного кода в объекте.
Синхронизированный в основном включает в себя два метода: синхронизированный метод и синхронизированный блок.
Синхронизированный метод
Объявите синхронизированный метод, добавив синхронизированное ключевое слово в объявление метода. нравиться:
общественный синхронизированный void getResult ();
Синхронизированный метод контролирует доступ к переменным члена класса. Как это избегает контроля доступа переменных членов класса? Мы знаем, что метод использует синхронизированное ключевое слово, чтобы указать, что метод заблокирован. Когда любой поток обращается к измененному методу, необходимо определить, являются ли другие потоки «исключительными». Каждый экземпляр класса соответствует блокировке. Каждый синхронизированный метод должен вызвать блокировку экземпляра класса метода, прежде чем его можно будет выполнить. В противном случае, нить, к которой он принадлежит, заблокирована. Как только метод будет выполнен, блокировка будет эксклюзивным. Замок не будет выпущен, пока не вернется из метода, а заблокированный поток может получить блокировку.
На самом деле, синхронизированный метод имеет недостатки. Если мы объявим большой метод синхронизированным, он сильно повлияет на эффективность. Если несколько потоков получают доступ к синхронизированному методу, то только один поток выполняет метод одновременно, в то время как другие потоки должны ждать. Однако, если метод не использует синхронизированный, все потоки могут выполнять его одновременно, сокращая общее время выполнения. Поэтому, если мы знаем, что метод не будет выполняться с помощью нескольких потоков или нет проблем обмена ресурсами, нам не нужно использовать синхронизированное ключевое слово. Но если мы должны использовать синхронизированное ключевое слово, то мы можем заменить синхронизированный метод синхронизированным блоком кода.
Синхронизированный блок
Синхронизированный кодовый блок играет ту же роль, что и синхронизированный метод, за исключением того, что он делает критическую область как можно более короткой. Другими словами, это защищает только необходимые общие данные, оставляя эту операцию для оставшихся длинных блоков кода. Синтаксис выглядит следующим образом:
Synchronized (Object) {// код, который позволяет управлять доступом} Если нам нужно использовать синхронизированное ключевое слово таким образом, мы должны использовать ссылку на объект в качестве параметра. Обычно мы часто используем этот параметр в качестве этого. Synchronized (this) {// код, который позволяет управлять доступом}Существует следующее понимание синхронизированного (это):
1. Когда два параллельных потока получают доступ к этому синхронизированному (этому) синхронизированному кодовому блоку в одном объекте объекта, в течение одного времени можно выполнить только один поток. Другой поток должен ждать, пока текущий поток выполнит этот кодовый блок, прежде чем он сможет выполнить кодовый блок.
2. Однако, когда один поток обращается к одному синхронизированному (это) кодовому блоку синхронизации объекта, другой поток все еще может получить доступ к несинхронизированному (это) блоку кода синхронизации в объекте.
3. Особенно важно, чтобы, когда поток обращается к синхронизированному (этой) блоке кода синхронизации объекта, другие потоки будут заблокированы от доступа к всем другим блокам кода синхронизации синхронизации (это) в объекте.
4. Третий пример также применяется к другим блокам кода синхронизации. То есть, когда поток обращается к синхронизированному (этой) кодовому блоку синхронизации объекта, он получает блокировку объекта этого объекта. В результате другие потоки доступ ко всем частям синхронного кода объекта объекта будут временно заблокированы.
Замок
Есть принцип «Приходите первым, приходите позже» в многопользовании Java, то есть тот, кто захватывает ключ первым, сначала будет использовать его. Мы знаем, что, чтобы избежать проблем с конкуренцией за ресурсы, Java использует механизмы синхронизации, чтобы избежать, а механизмы синхронизации контролируются с использованием концепции замков. Итак, как блокировки отражаются в программах Java? Здесь нам нужно выяснить две концепции:
Что такое замок? В повседневной жизни это герметик, добавленный в двери, коробки, ящики и другие объекты, которые мешают другим взглянуть или воровать, и играет защитную роль. То же самое верно на Java. Замки играют роль в защите объектов. Если поток исключительно занимает определенный ресурс, то не хотите использовать другие потоки, но хотите их использовать? Давайте поговорим об этом, когда я закончу использовать это!
В программе Java, запущенной, JVM должен координировать данные, разделяемые двумя типами потоков:
1. переменные экземпляра, сохраненные в куче
2. Переменные класса, сохраненные в области метода.
В виртуальной машине Java каждый объект и класс логически связаны с монитором. Для объекта связанный монитор защищает переменные экземпляра объекта. Для занятий мониторы защищают переменные класса. Если у объекта нет переменных экземпляра, или у класса нет переменных, связанный монитор ничего не будет контролировать.
Чтобы реализовать эксклюзивную возможность мониторинга монитора, Java Virtual Machine связывает блокировку для каждого объекта и класса. Представляет привилегии, которые может иметь только один поток в любое время. Темы не должны блокировать при доступе к переменным экземпляра или переменным класса. Если поток приобретает блокировку, другие потоки не могут получить ту же блокировку, прежде чем он выпустит блокировку. Поток может заблокировать один и тот же объект несколько раз. Для каждого объекта виртуальная машина Java поддерживает счетчик блокировки. Каждый раз, когда поток получает объект, счетчик увеличивается на 1, и каждый раз, когда он отпускается, счетчик уменьшается на 1. Когда значение счетчика равна 0, блокировка полностью отпускается.
Программистам Java не нужно добавлять замки сами по себе, блокировки объектов используются внутри виртуальных машин Java. В программе Java вам нужно использовать только синхронизированный блок или синхронизированный метод для обозначения области мониторинга. Всякий раз, когда вы вводите область мониторинга, виртуальная машина Java автоматически блокирует объект или класс.
Простой замок
При использовании синхронизации мы используем блокировки, как это:
public Class Threadtest {public void test () {synchronized (this) {// сделать что -нибудь}}}Синхронизированный гарантирует, что только один поток выполняет что -то в то же время. Вот использование блокировки вместо синхронизации:
public Class Threadtest {lock Lock = new Lock (); public void test () {lock.lock (); // сделать что -нибудь lock.unlock (); }}Метод lock () блокирует объект экземпляра блокировки, поэтому все потоки, которые вызывают метод lock () на объекте, будут заблокированы до тех пор, пока не будет вызван метод разблокировки () объекта блокировки.
Что заблокировано?
Перед этим вопросом мы должны быть ясны: добавляется ли синхронизированное ключевое слово в метод или объект, замок, который он приобретает, является объектом. В Java каждый объект может использоваться в качестве блокировки, который в основном отражается в следующих трех аспектах:
Для методов синхронизации блокировка является текущим объектом экземпляра.
Для блока метода синхронизации блокировка является объектом, настроенным в синхнизированных скобках.
Для методов статической синхронизации блокировка является объектом класса текущего объекта.
Во -первых, давайте посмотрим на следующий пример:
public Class Threadtest_01 реализует runnable {@Override public synchronized void run () {for (int i = 0; i <3; i ++) {System.out.println (thread.currentThread (). getName ()+"Run ......"); }} public static void main (string [] args) {for (int i = 0; i <5; i ++) {new Thread (new ThreadTest_01 (), "Thread_"+i) .start (); }}}Результаты частичного пробега:
Thread_2run ... thread_2run ... thread_4run ... thread_4run ... thread_3run ... thread_3run ... thread_3run ... thread_3run ... thread_2run ... thread_4run ...
Этот результат немного отличается от ожидаемого результата (эти потоки работают здесь). Логически говоря, метод прогона плюс синхронизированный ключевой слово будет создавать эффект синхронизации. Эти потоки должны выполнить метод запуска один за другим. Как упомянуто выше, после добавления синхронизированных ключевых слов к методу участника на самом деле является блокировкой метода участника. Конкретная точка состоит в том, чтобы использовать сам объект, где метод элемента расположен в качестве блокировки объекта. Однако в этом примере у нас есть новые 10 -тестовые объекты Threadtest, и каждый поток будет удерживать блокировку объекта собственного объекта потока, который определенно не даст синхронного эффекта. SO: если эти потоки должны быть синхронизированы, блокировки объекта, удерживаемые этими потоками, должны быть обмены и уникальны!
Какой объект синхронизирован заблокирован в это время? То, что он блокирует, - это назвать этот объект синхронного метода. То есть, если объект ThreadTest выполняет методы синхронизации в разных потоках, он будет образовывать взаимоисключающие. Достичь эффекта синхронизации. Так что измените вышеупомянутый новый поток (New ThreadTest_01 (), "thread_" + i) .start (); в новый поток (Threadtest, "Threat_" + i) .start ();
Для методов синхронизации блокировка является текущим объектом экземпляра.
Приведенный выше пример использует синхронизированный метод. Давайте посмотрим на блок синхронизированного кода:
public Class Threadtest_02 Extends Thread {Private String Lock; Приватное название строки; public threadtest_02 (name String, String Lock) {this.name = name; this.lock = lock; } @Override public void run () {synchronized (lock) {for (int i = 0; i <3; i ++) {System.out.println (name+"run ......"); }}} public static void main (string [] args) {string lock = new String ("test"); for (int i = 0; i <5; i ++) {new threadtest_02 ("threadtest_"+i, lock) .start (); }}}Результаты работы:
Threadtest_0 run ... threadtest_0 run ... threadtest_0 run ... threadtest_1 run ... threadtest_1 run ... threadtest_1 run ... threadtest_1 run ... threadtest_4 run ... threadtest_4 run ... threadtest_4 run ... threadtest_3 run ... threadtest_3 runtest run_3 runtest_2 runtest_2 runtest_2 runtest_2 runtest_2 runtest_2 runtest_2 runtest_2 runtest_2 runtest_2 runtest_2 runtest_2 runtest_2.
В основном методе мы создаем блокировку объекта строкового объекта и назначаем этот объект каждому блокировке подводной переменной объекта ThreadTest2. Мы знаем, что в Java есть пул струн, поэтому частные переменные этих потоков на самом деле указывают на ту же область в памяти кучи, то есть область, где хранятся переменные блокировки в основной функции, поэтому блокировка объекта является уникальной и общей. Синхронизация потока! !
Объект строки блокировки, который синхронизировал блокировки здесь.
Для блока метода синхронизации блокировка является объектом, настроенным в синхнизированных скобках.
public Class Threadtest_03 Extends Thread {public Synchronized Static void test () {for (int i = 0; i <3; i ++) {System.out.println (thread.currentThread (). getName ()+"run ......"); }} @Override public void run () {test (); } public static void main (string [] args) {for (int i = 0; i <5; i ++) {new ThreadTest_03 (). Start (); }}}Результаты работы:
Thread-0 запустить ... Thread-0 запустить ... Thread-0 запустить ... Thread-4 запустить ... Thread-4 запустить ... THINE-4 RUNG ... THING-1 RUNAL ... THIND-1 RUNGER ... THING-1 RUNGE ... THIND-2 Запустить ... THIND-2 Запустить ... Запуск потока 2 ... THING-3 запустить ... THING-3 RUN ... THING-3 RUNGE ...
В этом примере метод выполнения использует метод синхронизации и метод статической синхронизации. Так что же здесь синхронизированный замок? Мы знаем, что Static находится за пределами объекта и находится на уровне класса. Следовательно, блокировка объекта является экземпляром класса класса, где находится статический релиз. Поскольку в JVM все загруженные классы имеют уникальные объекты класса, единственный объект ThreadTest_03.Class в этом случае. Независимо от того, сколько экземпляров класса мы создаем, его экземпляр класса по -прежнему остается одним! Таким образом, блокировка объекта уникальна и общая. Синхронизация потока! !
Для методов статической синхронизации блокировка является объектом класса текущего объекта.
Если класс определяет синхронизированную статическую функцию A и синхронизированную функцию экземпляра B, то один и тот же объект OBJ этого класса не будет представлять собой синхронизацию при доступе к двум методам A и B в нескольких потоках, поскольку их замки различны. Замок метода a является объектом obj, в то время как блокировка B является классом, к которому принадлежит OBJ.
Обновление блокировки
В Java есть четыре штата: государство без блокировки, предвзятое состояние замка, легкое состояние замка и штат Leaflight Lock, что постепенно будет расти с конкуренцией. Замок может быть модернизирован, но не может быть понижен, что означает, что смещенная блокировка не может быть понижена до смещенного замка после обновления до легкого блокировки. Эта стратегия обновления блокировки, но не может быть понижена, состоит в том, чтобы повысить эффективность получения и освобождения замков. Основной частью ниже является краткое изложение блога: параллелизм (II) синхронизирован в Java SE1.6.
Блокировка спина
Мы знаем, что когда поток входит в метод синхронизации/блок кода, если он обнаруживает, что метод синхронизации/кодовый блок занят другими, он будет ждать и ввести состояние блокировки. Производительность этого процесса низкая.
Принимая во внимание конкуренцию замок или ожидая вещей, нить может быть менее стремится войти в состояние блокировки, но подождите и посмотрите, будет ли закрыт замок немедленно. Это замок. В определенной степени спин блокировки может оптимизировать поток.
Положительный замок
Положительные замки в основном используются для решения проблемы производительности замков без конкуренции. В большинстве случаев блокировки блокировки не только не имеют многопоточного конкурента, но и всегда получают несколько раз по одному и тому же потоку. Для того, чтобы приобретать резьбу приобретала замки по более низкой стоимости, вводятся смещенные замки. Когда поток получает блокировку, поток может заблокировать объект несколько раз, но каждый раз, когда выполняется такая операция, некоторое потребление накладных расходов из-за операции CAS (инструкция CPU Compare и Swap). Чтобы уменьшить эти накладные расходы, замок будет иметь тенденцию быть первым потоком, чтобы получить его. Если блокировка не получена другими потоками во время следующего процесса выполнения, поток, удерживающий смещенную блокировку, никогда не потребуется снова синхронизировать.
Когда другие потоки пытаются конкурировать за смещенную блокировку, нить удерживает смещенную блокировку, выпускает блокировку.
Расширение блокировки
Несколько или несколько вызовов с слишком маленькой гранулярностью не так эффективны, как вызовы замков с большим гранулярным замком.
Легкий замок
Основа для легкой блокировки для улучшения производительности синхронизации программы заключается в том, что «для большинства замков не существует конкуренции во всем цикле синхронизации», который является эмпирическим данных. Легкая блокировка создает пространство, называемое записи блокировки в рамке стека текущего потока, которое используется для хранения тока наказы и состояния объекта блокировки. Если нет конкуренции, легкий замок использует операцию CAS, чтобы избежать накладных расходов на использование Mutexes, но если есть конкуренция с замками, в дополнение к накладным расходам Mutexes, работа CAS происходит дополнительно, поэтому в случае соревнования, легкий замок будет медленнее, чем традиционный в тяжелом весе.
Справедливость замка
Противоположностью справедливости является голод. Так что же такое "голод"? Если поток не может получить время выполнения процессора, потому что другие потоки всегда занимают процессор, мы называем нить «голодным до смерти». Решение голода называется «справедливостью» - все темы могут получить возможности для работы процессора.
Есть несколько основных причин голода ниток:
Высокие нити потребляют время процессора всех низкоприоритетных нитей. Мы можем установить его приоритет индивидуально для каждого потока, от 1 до 10. Чем выше приоритетный поток, тем больше времени требуется для получения процессора. Для большинства приложений лучше не изменить свою приоритетную ценность.
Поток постоянно блокируется в состоянии, ожидающем входа в блок синхронизации. Синхронная область кода Java является важным фактором, который вызывает голод потока. Синхронные кодовые блоки Java не гарантируют порядок введенных потоков. Это означает, что в теории есть один или несколько потоков, которые всегда заблокированы при попытке ввести в область синхронного кода, потому что другие потоки всегда превосходят их, чтобы получить доступ, что приводит к тому, что он не получает возможности CPU и «голодает до смерти».
Поток ждет объекта, который само по себе также навсегда ждет завершения. Если несколько потоков выполняются методом wait (), а вызов notify () на нем не гарантирует, что любой поток будет пробужден, любой поток может находиться в состоянии непрерывного ожидания. Следовательно, существует риск, что одна ожидающая потока никогда не будет пробуждена, потому что другие потоки ожидания всегда могут быть пробуждены.
Чтобы решить проблему потока «голода», мы можем использовать замки для достижения справедливости.
Воспроизведение замков
Мы знаем, что, когда поток запрашивает объект, который удерживает блокировку другим потоком, поток заблокируется, но может ли он быть успешным, когда поток запрашивает объект, который сам содержит блокировку? Ответ заключается в том, что успех может быть успешным, и гарантией успеха является «повторное введение» замков потоков.
«Повторный» означает, что вы можете снова получить свой собственный внутренний блокировка без блокировки. следующее:
открытый класс отец {public synchronized void method () {// сделать что -то}} public class chall расширяет отца {public synchronized void method () {// делать что -то супер.method (); }}
Если он не будет повторно, приведенный выше код будет запущенным, потому что вызов Child's Method () сначала приобретет встроенный замок отца родительского класса, а затем приобретет встроенный замок ребенка. При вызове метода родительского класса вам нужно снова вернуться к встроенной блокировке родительского класса. Если это не повторно, вы можете попасть в тупик.
Реализация повторной обработки многопоточного чтения Java заключается в том, чтобы связать расчет запроса и поток, который занимает его по каждой блокировке. Когда количество составляет 0, считается, что замок не занят, и тогда любой поток может получить право собственности на замк. Когда поток успешно запрашивается, JVM будет записывать поток, удерживающий блокировку и установит счет на 1. Если другие потоки запрашивают блокировку, они должны ждать. Когда поток запрашивается, чтобы снова получить блокировку, счет будет +1; Когда поток занятий выходит из блока синхронного кода, счет будет -1, пока он не станет 0, блокировка будет выпущена. Только тогда другие потоки могут иметь возможность получить право собственности на замок.
Lock и его класс внедрения
java.util.concurrent.locks обеспечивает очень гибкий механизм блокировки, обеспечивая интерфейс и класс структуры для блокировки и условий ожидания. Он отличается от встроенной синхронизации и мониторов, что обеспечивает большую гибкость в использовании блокировки и условий. Его диаграмма структуры класса выглядит следующим образом:
Reentrantlock: переосмысление Mutex Lock, основная реализация интерфейса блокировки.
ReenterTreadWritelock:
ReadWritelock: ReadWritelock поддерживает пару связанных замков, один для операций только для чтения, а другой для операций записи.
Семфор: счетный семафор.
Условие: Цель блокировки состоит в том, чтобы позволить потоке приобрести блокировку и посмотреть, выполнено ли определенное условие ожидания.
Cyclicbarrier: синхронный вспомогательный класс, который позволяет группе потоков ждать друг друга, пока не достигнет общей барьерной точки.