Можно ли упростить синхронизированный блок до блока Try-Finally на уровне байт-кода?
Когда я пишу свой собственный компилятор для Java-подобного языка, у меня возникают проблемы с компиляцией synchronized blocks
. Я пришел к следующей идее, чтобы упростить их до блоков try-finally
:
synchonized (obj) {
statements...
}
Можно заменить на
Object _lock = obj
_monitorEnter(lock)
try {
statements...
}
finally {
_monitorExit(lock)
}
Где _monitorEnter
и _monitorExit
представляют инструкции MONITORENTER
и MONITOREXIT
.
synchronized
компилируется, или я что-то упускаю?
править
Моя реализация ранее имела некоторые специальные обработка для операторов return
и throw
в теле. В принципе, он будет вручную загружать все переменные lock
и MONITOREXIT
перед каждой инструкцией *RETURN
или THROW
. Это обрабатывается блоком finally
, или мне все еще нужны эти проверки?
2 ответа:
Ваши предположения верны. Блок
synchronized
в языке Java реализуется с помощью инструкцийmonitorenter
иmonitorexit
. Вы можете просмотреть детали спецификации JVM здесь.Синхронизация в виртуальной машине Java осуществляется монитором вход и выход, либо явно (с помощью монитора и инструкции monitorexit) или неявно (с помощью вызова метода и инструкции по возврату).
Компилятор генерирует байт-код, который будет обрабатывать все исключения, брошенные внутри тела
synchronized
, поэтому ваш подход try-finally будет работать нормально.Спецификация Инструкции
finally
ничего не говорит о выпуске мониторов. Пример, приведенный в первой ссылке, показывает байт-код для простого метода, обернутого в блокsynchronized
. Как вы можете видеть, любое возможное исключение обрабатывается для обеспечения выполнения команды monitorexit. Вы должны реализовать такое же поведение в своем компиляторе (написать код, который выпустит монитор внутри наконец заявление).void onlyMe(Foo f) { synchronized(f) { doSomething(); } } Method void onlyMe(Foo) 0 aload_1 // Push f 1 dup // Duplicate it on the stack 2 astore_2 // Store duplicate in local variable 2 3 monitorenter // Enter the monitor associated with f 4 aload_0 // Holding the monitor, pass this and... 5 invokevirtual #5 // ...call Example.doSomething()V 8 aload_2 // Push local variable 2 (f) 9 monitorexit // Exit the monitor associated with f 10 goto 18 // Complete the method normally 13 astore_3 // In case of any throw, end up here 14 aload_2 // Push local variable 2 (f) 15 monitorexit // Be sure to exit the monitor! 16 aload_3 // Push thrown value... 17 athrow // ...and rethrow value to the invoker 18 return // Return in the normal case Exception table: From To Target Type 4 10 13 any 13 16 13 any
Компилятор Java компилирует синхронизированные блоки во что-то вроде try-finally, как вы уже догадались. Однако есть одно незначительное отличие-обработка исключений ловит исключения, вызванные
monitorexit
, и бесконечно петляет, пытаясь освободить блокировку. Нет никакого способа, чтобы указать поток управления достаточно, как это в Java.