Как виртуалната машина Java извършва синхронизация на нишки

Всички програми на Java се компилират в файлове с класове, които съдържат байт кодове, машинния език на виртуалната машина Java. Тази статия разглежда как синхронизирането на нишки се обработва от виртуалната машина Java, включително съответните байт кодове. (1750 думи)

Този месец Under The Hood разглежда синхронизацията на нишки както на езика Java, така и на виртуалната машина Java (JVM). Тази статия е последната от дългата поредица статии за байт кодове, които започнах миналото лято. Той описва единствените два операционни кода, пряко свързани със синхронизацията на нишките, използваните за влизане и излизане от монитори.

Конци и споделени данни

Една от силните страни на езика за програмиране Java е неговата поддръжка за многопоточност на езиково ниво. Голяма част от тази подкрепа се концентрира върху координирането на достъпа до данни, споделени между множество нишки.

JVM организира данните на работещо Java приложение в няколко области с данни по време на изпълнение: един или повече Java стекове, купчина и област на метод. За фона на тези области на паметта вижте първата статия Under Hood : „Слабата, означаваща виртуална машина“.

Вътре в Java виртуалната машина на всяка нишка се присъжда Java стек , който съдържа данни, до които никоя друга нишка няма достъп, включително локалните променливи, параметри и връщаните стойности на всеки метод, който нишката е извикала. Данните в стека са ограничени до примитивни типове и препратки към обекти. В JVM не е възможно да се постави изображението на действителен обект в стека. Всички обекти се намират в купчината.

В JVM има само една купчина и всички нишки го споделят. Купчината не съдържа нищо друго освен обекти. Няма начин да поставите самотен примитивен тип или препратка към обект върху купчината - тези неща трябва да са част от обект. Масивите се намират в купчината, включително масиви от примитивни типове, но в Java масивите също са обекти.

Освен стека на Java и купчината, другите данни за място могат да се намират в JVM е областта на метода , която съдържа всички променливи на класа (или статични), използвани от програмата. Областта на метода е подобна на стека, тъй като съдържа само примитивни типове и препратки към обекти. За разлика от стека обаче променливите на класа в областта на метода се споделят от всички нишки.

Заключване на обекти и класове

Както е описано по-горе, две области с памет във виртуалната машина Java съдържат данни, споделени от всички нишки. Това са:

  • Купчината, която съдържа всички обекти
  • Областта на метода, която съдържа всички променливи на класа

Ако няколко нишки трябва да използват едни и същи обекти или променливи на класа едновременно, достъпът им до данните трябва да се управлява правилно. В противен случай програмата ще има непредсказуемо поведение.

За да координира споделен достъп до данни между множество нишки, виртуалната машина Java свързва заключване с всеки обект и клас. Заключването е като привилегия, която само една нишка може да притежава едновременно. Ако дадена нишка иска да заключи определен обект или клас, тя пита JVM. В даден момент, след като нишката поиска от JVM заключване - може би много скоро, може би по-късно, вероятно никога - JVM дава заключването на нишката. Когато нишката вече не се нуждае от ключалката, тя я връща в JVM. Ако друга нишка е поискала същото заключване, JVM предава заключването на тази нишка.

Бравите на класове всъщност се изпълняват като обектни ключалки. Когато JVM зарежда файл с клас, той създава екземпляр на клас java.lang.Class. Когато заключите клас, вие всъщност заключвате Classобекта на този клас .

Нишките не трябва да получават заключване за достъп до променливи на инстанция или клас. Ако една нишка все пак получи заключване, никоя друга нишка няма достъп до заключените данни, докато нишката, която притежава ключалката, не я освободи.

Монитори

JVM използва ключалки заедно с монитори . Мониторът по същество е пазител, тъй като наблюдава последователност от кодове, като се уверява, че само една нишка наведнъж изпълнява кода.

Всеки монитор е свързан с препратка към обект. Когато нишка пристигне при първата инструкция в блок от код, който е под зоркото око на монитора, нишката трябва да получи заключване на референтния обект. Нишката няма право да изпълнява кода, докато не получи заключването. След като получи заключването, нишката влиза в блока на защитения код.

Когато нишката напусне блока, без значение как напуска блока, освобождава заключването на свързания обект.

Множество ключалки

Една нишка има право да заключва един и същ обект няколко пъти. За всеки обект JVM поддържа отчитане на броя заключения на обекта. Отключен обект има нула. Когато нишка придобие ключалката за първи път, броят се увеличава до един. Всеки път, когато нишката придобие заключване на същия обект, броят се увеличава. Всеки път, когато нишката освободи ключалката, броят се намалява. Когато броят достигне нула, заключването се освобождава и се предоставя на други нишки.

Синхронизирани блокове

В терминологията на езика Java координацията на множество нишки, които трябва да имат достъп до споделени данни, се нарича синхронизация . Езикът предоставя два вградени начина за синхронизиране на достъпа до данни: със синхронизирани изявления или синхронизирани методи.

Синхронизирани изявления

За да създадете синхронизиран оператор, използвате synchronizedключовата дума с израз, който изчислява препратка към обект, както в reverseOrder()метода по-долу:

class KitchenSync { private int[] intArray = new int[10]; void reverseOrder() { synchronized (this) { int halfWay = intArray.length / 2; for (int i = 0; i < halfWay; ++i) { int upperIndex = intArray.length - 1 - i; int save = intArray[upperIndex]; intArray[upperIndex] = intArray[i]; intArray[i] = save; } } } }

В горния случай изразите, съдържащи се в синхронизирания блок, няма да бъдат изпълнени, докато не се получи заключване на текущия обект ( this). Ако вместо thisпрепратка, изразът даде препратка към друг обект, заключването, свързано с този обект, ще бъде придобито преди потокът да продължи.

Два операционни кода monitorenterи monitorexitсе използват за синхронизиращи блокове в методите, както е показано в таблицата по-долу.

Таблица 1. Монитори

Opcode Операнд (и) Описание
monitorenter нито един pop objectref, придобийте ключалката, свързана с objectref
monitorexit нито един pop objectref, освободете заключването, свързано с objectref

Когато monitorenterсе срещне от виртуалната машина Java, тя получава заключване за обекта, посочен от objectref в стека. Ако нишката вече притежава ключалката за този обект, броят се увеличава. Всеки път, когато monitorexitсе изпълнява за нишката на обекта, броят се намалява. Когато броят достигне нула, мониторът се освобождава.

Погледнете последователността на байт кода, генерирана от reverseOrder()метода на KitchenSyncкласа.

Note that a catch clause ensures the locked object will be unlocked even if an exception is thrown from within the synchronized block. No matter how the synchronized block is exited, the object lock acquired when the thread entered the block definitely will be released.

Synchronized methods

To synchronize an entire method, you just include the synchronized keyword as one of the method qualifiers, as in:

class HeatSync { private int[] intArray = new int[10]; synchronized void reverseOrder() { int halfWay = intArray.length / 2; for (int i = 0; i < halfWay; ++i) { int upperIndex = intArray.length - 1 - i; int save = intArray[upperIndex]; intArray[upperIndex] = intArray[i]; intArray[i] = save; } } }

The JVM does not use any special opcodes to invoke or return from synchronized methods. When the JVM resolves the symbolic reference to a method, it determines whether the method is synchronized. If it is, the JVM acquires a lock before invoking the method. For an instance method, the JVM acquires the lock associated with the object upon which the method is being invoked. For a class method, it acquires the lock associated with the class to which the method belongs. After a synchronized method completes, whether it completes by returning or by throwing an exception, the lock is released.

Coming next month

Now that I have gone through the entire bytecode instruction set, I will be broadening the scope of this column to include various aspects or applications of Java technology, not just the Java virtual machine. Next month, I'll begin a multi-part series that gives an in-depth overview of Java's security model.

Бил Венърс пише софтуер професионално от 12 години. Базиран в Силициевата долина, той предоставя софтуерни консултации и услуги за обучение под името Artima Software Company. През годините той е разработил софтуер за потребителската електроника, образованието, полупроводниците и животозастраховането. Програмирал е на много езици на много платформи: асемблерен език на различни микропроцесори, C на Unix, C ++ на Windows, Java в мрежата. Автор е на книгата „Inside the Java Virtual Machine“, издадена от McGraw-Hill.

Научете повече за тази тема

  • The book The Java virtual machine Specification (//www.aw.com/cp/lindholm-yellin.html), by Tim Lindholm and Frank Yellin (ISBN 0-201-63452-X), part of The Java Series (//www.aw.com/cp/javaseries.html), from Addison-Wesley, is the definitive Java virtual machine reference.
  • Previous "Under The Hood" articles:
  • "The Lean, Mean Virtual Machine" Gives an introduction to the Java virtual machine.
  • "The Java Class File Lifestyle" Gives an overview to the Java class file, the file format into which all Java programs are compiled.
  • "Java's Garbage-Collected Heap" Gives an overview of garbage collection in general and the garbage-collected heap of the Java virtual machine in particular.
  • "Bytecode Basics" Introduces the bytecodes of the Java virtual machine, and discusses primitive types, conversion operations, and stack operations in particular.
  • "Floating Point Arithmetic" Describes the Java virtual machine's floating-point support and the bytecodes that perform floating point operations.
  • "Logic and Arithmetic" Describes the Java virtual machine's support for logical and integer arithmetic, and the related bytecodes.
  • "Objects and Arrays" Describes how the Java virtual machine deals with objects and arrays, and discusses the relevant bytecodes.
  • "Exceptions" Describes how the Java virtual machine deals with exceptions, and discusses the relevant bytecodes.
  • "Опитайте-накрая" Описва как виртуалната машина на Java прилага клаузи за опита-накрая и обсъжда съответните байтови кодове.
  • "Контрол на потока" Описва как виртуалната машина на Java изпълнява контролен поток и обсъжда съответните байт кодове.
  • "Архитектурата на Aglets" описва вътрешната работа на Aglets, автономната технология на IBM за софтуерни агенти, базирана на Java.
  • "The Point of Aglets" анализира реалната полезност на мобилните агенти като Aglets, автономната технология на IBM за софтуерни агенти, базирана на Java.
  • „Извикване и връщане на метод“ Обяснява как виртуалната машина Java се извиква и връща от методи, включително съответните байт кодове.

Тази история „Как виртуалната машина Java извършва синхронизация на нишките“ първоначално е публикувана от JavaWorld.