synchronized
, является очень важным ключевым словом в Java для разрешения доступа к синхронизации данных в параллельных ситуациях. Когда мы хотим гарантировать, что общий ресурс доступен только одному потоку за раз, мы можем использовать его в нашем коде.synchronized
Ключевое слово блокирует класс или объект. Итак, эта статья познакомитsynchronized
Каков принцип реализации ключевых слов. Перед прочтением этой статьи рекомендуется прочитатьКак виртуальная машина Java выполняет синхронизацию потоков.
декомпилировать
Как мы все знаем, в Javasynchronized
Существует две формы использования: синхронизированные методы и синхронизированные блоки кода. код показывает, как показано ниже:
/**
* @author Hollis 17/11/9.
*/
public class SynchronizedTest {
public synchronized void doSth(){
System.out.println("Hello World");
}
public void doSth1(){
synchronized (SynchronizedTest.class){
System.out.println("Hello World");
}
}
}
Давайте использоватьJavapПри декомпиляции приведенного выше кода получается следующее (некоторая бесполезная информация отфильтровывается):
public synchronized void doSth();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello World
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
public void doSth1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: ldc #5 // class com/hollis/SynchronizedTest
2: dup
3: astore_1
4: monitorenter
5: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #3 // String Hello World
10: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: aload_1
14: monitorexit
15: goto 23
18: astore_2
19: aload_1
20: monitorexit
21: aload_2
22: athrow
23: return
декомпилироватьПосле этого мы можем увидеть байт-код, сгенерированный для нас компилятором Java. заdoSth
а такжеdoSth1
обрабатывается несколько иначе. То есть. JVM по-разному обрабатывает синхронизированные методы и синхронизированные блоки кода.
Для синхронизированных методов JVM используетACC_SYNCHRONIZED
маркер для достижения синхронизации. Для синхронизированных кодовых блоков. JVM используетmonitorenter
,monitorexit
Две инструкции для достижения синхронизации.
Для этой части вСпецификация JVMСоответствующее описание также можно найти в .
Синхронный метод
The Java® Virtual Machine SpecificationВведение в синхронизацию на уровне методов:
Method-level synchronization is performed implicitly, as part of method invocation and return. A synchronized method is distinguished in the run-time constant pool's method_info structure by the ACC_SYNCHRONIZED flag, which is checked by the method invocation instructions. When invoking a method for which ACC_SYNCHRONIZED is set, the executing thread enters a monitor, invokes the method itself, and exits the monitor whether the method invocation completes normally or abruptly. During the time the executing thread owns the monitor, no other thread may enter it. If an exception is thrown during invocation of the synchronized method and the synchronized method does not handle the exception, the monitor for the method is automatically exited before the exception is rethrown out of the synchronized method.
Суть в том, что синхронизация на уровне метода является неявной. В постоянном пуле синхронизированного метода будет одинACC_SYNCHRONIZED
логотип. Когда поток хочет получить доступ к методу, он проверяет, существует лиACC_SYNCHRONIZED
, если он установлен, вам необходимо сначала получить блокировку монитора, затем начать выполнение метода и снять блокировку монитора после выполнения метода. В это время, если другие потоки запросят выполнение метода, они будут заблокированы, поскольку блокировка монитора не может быть получена. Стоит отметить, что если во время выполнения метода возникает исключение, и оно не обрабатывается внутри метода, блокировка монитора будет автоматически снята до того, как исключение будет выброшено за пределы метода.
Блок синхронизированного кода
Использование синхронизированного блока кодаmonitorenter
а такжеmonitorexit
Реализованы две инструкции.The Java® Virtual Machine SpecificationВведение в эти две команды:
monitorenter
Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:
If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.
If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.
If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership.
monitorexit
The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.
The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.
Общее содержание таково: Вы можете выполнитьmonitorenter
Инструкция понимается как блокировка, выполнениеmonitorexit
Это понимается как снятие блокировки. Каждый объект поддерживает счетчик количества раз, когда он был заблокирован. Этот счетчик разблокированных объектов равен 0, когда поток получает блокировку (выполняетmonitorenter
), счетчик увеличивается до 1, и когда тот же поток снова получает блокировку объекта, счетчик снова увеличивается. Когда тот же поток освобождает блокировку (выполняетсяmonitorexit
инструкция), счетчик снова уменьшается. когда счетчик равен 0. Блокировка будет снята, и другие потоки смогут получить блокировку.
Суммировать
Синхронный метод через
ACC_SYNCHRONIZED
Ключевое слово неявно блокирует метод. Когда метод, который должен быть выполнен потоком, помеченACC_SYNCHRONIZED
, перед выполнением метода необходимо получить блокировку.Блок синхронизированного кода через
monitorenter
а такжеmonitorexit
Выполнить, чтобы заблокировать. Когда поток выполняется дляmonitorenter
Когда блокировка получена, можно выполнить следующий метод. Когда поток выполняется дляmonitorexit
Когда замок снят.Каждый объект поддерживает счетчик количества раз, когда он был заблокирован.Если значение счетчика равно 0, это означает, что блокировка может быть получена любым потоком. Когда счетчик не равен 0, только поток, получивший блокировку, может снова получить блокировку. для повторного входа в замок.
Пока что у нас есть общее понимание принципа Synchronized. Но есть еще несколько вопросов, которые не были четко сформулированы, например, что такое Monitor? Где хранится состояние блокировки объекта? Не волнуйтесь, я представлю его позже.