Bagaimana mesin virtual Java melakukan sinkronisasi utas

Semua program Java dikompilasi menjadi file kelas, yang berisi bytecode, bahasa mesin dari mesin virtual Java. Artikel ini membahas bagaimana sinkronisasi thread ditangani oleh mesin virtual Java, termasuk bytecode yang relevan. (1.750 kata)

Under The Hood bulan ini membahas sinkronisasi utas dalam bahasa Java dan mesin virtual Java (JVM). Artikel ini adalah yang terakhir dari seri panjang artikel bytecode yang saya mulai musim panas lalu. Ini menjelaskan hanya dua opcode yang terkait langsung dengan sinkronisasi utas, opcode yang digunakan untuk masuk dan keluar monitor.

Untaian dan data bersama

Salah satu kekuatan bahasa pemrograman Java adalah dukungannya terhadap multithreading pada level bahasa. Banyak dari dukungan ini berpusat pada koordinasi akses ke data yang dibagikan di antara banyak utas.

JVM mengatur data aplikasi Java yang sedang berjalan ke dalam beberapa area data runtime: satu atau beberapa tumpukan Java, heap, dan area metode. Untuk latar belakang area memori ini, lihat artikel Under the Hood yang pertama : "Mesin virtual yang ramping dan berarti".

Di dalam mesin virtual Java, setiap thread diberikan tumpukan Java , yang berisi data yang tidak dapat diakses oleh thread lain, termasuk variabel lokal, parameter, dan nilai kembalian dari setiap metode yang dipanggil oleh thread. Data di tumpukan terbatas pada jenis primitif dan referensi objek. Di JVM, tidak mungkin menempatkan gambar objek yang sebenarnya di tumpukan. Semua objek berada di heap.

Hanya ada satu heap di dalam JVM, dan semua thread membagikannya. Heap hanya berisi objek. Tidak ada cara untuk menempatkan tipe primitif soliter atau referensi objek pada heap - benda ini harus menjadi bagian dari sebuah objek. Array berada di heap, termasuk array dengan tipe primitif, tetapi di Java, array juga merupakan objek.

Selain tumpukan Java dan heap, data tempat lain yang mungkin berada di JVM adalah area metode , yang berisi semua variabel kelas (atau statis) yang digunakan oleh program. Area metode mirip dengan tumpukan karena hanya berisi tipe primitif dan referensi objek. Namun, tidak seperti tumpukan, variabel kelas di area metode dibagikan oleh semua utas.

Kunci objek dan kelas

Seperti dijelaskan di atas, dua area memori di mesin virtual Java berisi data yang dibagikan oleh semua utas. Ini adalah:

  • Heap, yang berisi semua objek
  • Area metode, yang berisi semua variabel kelas

Jika beberapa utas perlu menggunakan objek atau variabel kelas yang sama secara bersamaan, aksesnya ke data harus dikelola dengan benar. Jika tidak, program akan memiliki perilaku yang tidak terduga.

Untuk mengoordinasikan akses data bersama di antara beberapa utas, mesin virtual Java mengaitkan kunci dengan setiap objek dan kelas. Kunci adalah seperti hak istimewa yang hanya dapat "dimiliki" satu utas pada satu waktu. Jika utas ingin mengunci objek atau kelas tertentu, utas meminta JVM. Di beberapa titik setelah utas meminta JVM untuk mengunci - mungkin segera, mungkin nanti, mungkin tidak pernah - JVM memberikan kunci ke utas. Saat utas tidak lagi membutuhkan kunci, utas mengembalikannya ke JVM. Jika utas lain meminta kunci yang sama, JVM meneruskan kunci ke utas itu.

Kunci kelas sebenarnya diimplementasikan sebagai kunci objek. Ketika JVM memuat file kelas, itu membuat instance kelas java.lang.Class. Saat Anda mengunci kelas, Anda sebenarnya mengunci objek kelas itu Class.

Thread tidak perlu mendapatkan kunci untuk mengakses instance atau variabel kelas. Namun, jika utas mendapatkan kunci, tidak ada utas lain yang dapat mengakses data terkunci hingga utas yang memiliki kunci melepaskannya.

Monitor

JVM menggunakan kunci dalam hubungannya dengan monitor . Monitor pada dasarnya adalah penjaga yang mengawasi urutan kode, memastikan hanya satu utas pada satu waktu yang menjalankan kode.

Setiap monitor dikaitkan dengan referensi objek. Ketika utas tiba di instruksi pertama dalam blok kode yang berada di bawah pengawasan monitor, utas harus mendapatkan kunci pada objek yang direferensikan. Utas tidak diizinkan untuk mengeksekusi kode sampai kode tersebut mendapatkan kunci. Setelah mendapatkan kunci, utas memasuki blok kode yang dilindungi.

Ketika utas meninggalkan blok, tidak peduli bagaimana benang meninggalkan blok, itu melepaskan kunci pada objek terkait.

Beberapa kunci

Untaian tunggal diizinkan untuk mengunci objek yang sama beberapa kali. Untuk setiap objek, JVM mempertahankan hitungan berapa kali objek telah dikunci. Objek yang tidak terkunci memiliki hitungan nol. Saat utas memperoleh kunci untuk pertama kalinya, hitungan bertambah menjadi satu. Setiap kali utas mendapatkan kunci pada objek yang sama, hitungan bertambah. Setiap kali utas melepaskan kunci, jumlahnya berkurang. Saat hitungan mencapai nol, kuncinya dilepaskan dan tersedia untuk utas lain.

Blok tersinkronisasi

Dalam terminologi bahasa Java, koordinasi beberapa utas yang harus mengakses data bersama disebut sinkronisasi . Bahasa ini menyediakan dua cara bawaan untuk menyinkronkan akses ke data: dengan pernyataan tersinkronisasi atau metode tersinkronisasi.

Pernyataan tersinkronisasi

Untuk membuat pernyataan tersinkronisasi, Anda menggunakan synchronizedkata kunci dengan ekspresi yang mengevaluasi ke referensi objek, seperti pada reverseOrder()metode di bawah ini:

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; } } } }

Dalam kasus di atas, pernyataan yang terdapat dalam blok tersinkronisasi tidak akan dijalankan hingga kunci diperoleh pada objek saat ini ( this). Jika alih-alih thisreferensi, ekspresi menghasilkan referensi ke objek lain, kunci yang terkait dengan objek itu akan diperoleh sebelum utas dilanjutkan.

Dua opcode, monitorenterdan monitorexit, digunakan untuk blok sinkronisasi di dalam metode, seperti yang ditunjukkan pada tabel di bawah.

Tabel 1. Monitor

Opcode Operand (s) Deskripsi
monitorenter tidak ada pop objectref, dapatkan kunci yang terkait dengan objectref
monitorexit tidak ada pop objectref, lepaskan kunci yang terkait dengan objectref

Saat monitorenterditemui oleh mesin virtual Java, ia memperoleh kunci untuk objek yang dirujuk oleh objectref di tumpukan. Jika utas sudah memiliki kunci untuk objek itu, hitungan akan bertambah. Setiap kali monitorexitdijalankan untuk utas pada objek, jumlah dikurangi. Saat hitungan mencapai nol, monitor dilepaskan.

Lihatlah urutan bytecode yang dihasilkan oleh reverseOrder()metode KitchenSynckelas.

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.

Bill Venners telah menulis perangkat lunak secara profesional selama 12 tahun. Berbasis di Silicon Valley, dia menyediakan layanan konsultasi dan pelatihan perangkat lunak dengan nama Artima Software Company. Selama bertahun-tahun ia telah mengembangkan perangkat lunak untuk elektronik konsumen, pendidikan, semikonduktor, dan industri asuransi jiwa. Dia telah memprogram dalam banyak bahasa di banyak platform: bahasa assembly di berbagai mikroprosesor, C di Unix, C ++ di Windows, Java di Web. Dia adalah penulis buku: Inside the Java Virtual Machine, diterbitkan oleh McGraw-Hill.

Pelajari lebih lanjut tentang topik ini

  • 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.
  • "Try-Akhirnya" Menjelaskan bagaimana mesin virtual Java mengimplementasikan klausa coba-akhirnya, dan membahas bytecode yang relevan.
  • "Control Flow" Menjelaskan bagaimana mesin virtual Java mengimplementasikan aliran kontrol dan membahas bytecode yang relevan.
  • "The Architecture of Aglets" Menjelaskan cara kerja Aglets, teknologi agen perangkat lunak berbasis Java yang otonom dari IBM.
  • "The Point of Aglets" Menganalisis utilitas dunia nyata dari agen seluler seperti Aglets, teknologi agen perangkat lunak berbasis Java yang otonom dari IBM.
  • "Pemanggilan dan Pengembalian Metode" Menjelaskan bagaimana mesin virtual Java memanggil dan mengembalikan dari metode, termasuk bytecode yang relevan.

Cerita ini, "Bagaimana mesin virtual Java melakukan sinkronisasi thread" pada awalnya diterbitkan oleh JavaWorld.