arsitektur perangkat keras memori
Arsitektur perangkat keras memori modern berbeda dari model memori internal Java. Oleh karena itu, Anda perlu memahami arsitektur perangkat keras untuk mengetahui cara kerja model Java dengannya. Bagian ini menjelaskan arsitektur perangkat keras memori umum, dan bagian berikutnya menjelaskan cara kerja Java dengannya.
Berikut adalah diagram sederhana dari arsitektur perangkat keras komputer modern:
Di dunia modern, komputer memiliki 2 prosesor atau lebih dan ini sudah menjadi norma. Beberapa prosesor ini mungkin juga memiliki banyak core. Pada komputer seperti itu, dimungkinkan untuk menjalankan banyak utas secara bersamaan. Setiap inti prosesor mampu mengeksekusi satu utas pada waktu tertentu. Ini berarti bahwa setiap aplikasi Java adalah apriori multi-utas, dan dalam program Anda, satu utas per inti prosesor dapat berjalan pada satu waktu.
Inti prosesor berisi sekumpulan register yang berada di memorinya (di dalam inti). Itu melakukan operasi pada data register jauh lebih cepat daripada pada data yang berada di memori utama komputer (RAM). Ini karena prosesor dapat mengakses register ini lebih cepat.
Setiap CPU juga dapat memiliki lapisan cache sendiri. Sebagian besar prosesor modern memilikinya. Prosesor dapat mengakses cache-nya jauh lebih cepat daripada memori utama, tetapi tidak secepat register internalnya. Nilai kecepatan akses cache kira-kira antara kecepatan akses memori utama dan register internal.
Apalagi prosesor memiliki tempat untuk memiliki cache multi-level. Tapi ini tidak begitu penting untuk diketahui untuk memahami bagaimana model memori Java berinteraksi dengan memori perangkat keras. Penting untuk diketahui bahwa prosesor mungkin memiliki beberapa level cache.
Setiap komputer juga mengandung RAM (area memori utama) dengan cara yang sama. Semua core dapat mengakses memori utama. Area memori utama biasanya jauh lebih besar daripada memori cache inti prosesor.
Pada saat prosesor membutuhkan akses ke memori utama, ia membaca sebagian darinya ke dalam memori cache. Itu juga dapat membaca beberapa data dari cache ke dalam register internalnya dan kemudian melakukan operasi pada mereka. Ketika CPU perlu menulis kembali hasilnya ke memori utama, itu akan membuang data dari register internal ke cache, dan pada titik tertentu, ke memori utama.
Data yang disimpan dalam cache biasanya dibilas kembali ke memori utama saat prosesor perlu menyimpan sesuatu yang lain di dalam cache. Cache memiliki kemampuan untuk menghapus memorinya dan menulis data pada saat yang bersamaan. Prosesor tidak perlu membaca atau menulis cache penuh setiap kali melakukan pembaruan. Biasanya cache diperbarui dalam blok kecil memori, mereka disebut "cache line". Satu atau lebih "baris cache" dapat dibaca ke dalam memori cache, dan satu atau lebih baris cache dapat dibilas kembali ke memori utama.
Menggabungkan model memori Java dan arsitektur perangkat keras memori
Seperti yang telah disebutkan, model memori Java dan arsitektur perangkat keras memori berbeda. Arsitektur perangkat keras tidak membedakan antara tumpukan dan tumpukan utas. Pada perangkat keras, thread stack dan HEAP (tumpukan) berada di memori utama.
Bagian tumpukan dan tumpukan utas terkadang ada di cache dan register internal CPU. Ini ditunjukkan dalam diagram:
Saat objek dan variabel dapat disimpan di berbagai area memori komputer, masalah tertentu dapat muncul. Inilah dua yang utama:
- Visibilitas perubahan yang dibuat utas pada variabel bersama.
- Kondisi balapan saat membaca, memeriksa, dan menulis variabel bersama.
Kedua masalah ini akan dijelaskan di bawah ini.
Visibilitas Objek yang Dibagikan
Jika dua atau lebih utas berbagi objek tanpa menggunakan deklarasi atau sinkronisasi volatil yang tepat, maka perubahan pada objek bersama yang dibuat oleh satu utas mungkin tidak terlihat oleh utas lainnya.
Bayangkan objek yang dibagikan pada awalnya disimpan di memori utama. Utas yang berjalan di CPU membaca objek bersama ke dalam cache CPU yang sama. Di sana dia membuat perubahan pada objek. Hingga cache CPU telah dibilas ke memori utama, versi modifikasi dari objek bersama tidak dapat dilihat oleh utas yang berjalan di CPU lain. Dengan demikian, setiap utas dapat memperoleh salinannya sendiri dari objek yang dibagikan, setiap salinan akan berada dalam cache CPU yang terpisah.
Diagram berikut mengilustrasikan garis besar situasi ini. Satu utas yang berjalan di CPU kiri menyalin objek bersama ke dalam cache-nya dan mengubah nilai hitungan menjadi 2. Perubahan ini tidak terlihat oleh utas lain yang berjalan di CPU kanan karena pembaruan untuk menghitung belum dibilas kembali ke memori utama.
Untuk mengatasi masalah ini, Anda dapat menggunakan kata kunci volatile saat mendeklarasikan variabel. Itu dapat memastikan bahwa variabel yang diberikan dibaca langsung dari memori utama dan selalu ditulis kembali ke memori utama saat diperbarui.
Kondisi balapan
Jika dua atau lebih utas berbagi objek yang sama dan lebih dari satu utas memperbarui variabel dalam objek bersama itu, maka kondisi balapan dapat terjadi.
Bayangkan bahwa utas A membaca variabel hitungan objek bersama ke dalam cache prosesornya. Bayangkan juga bahwa utas B melakukan hal yang sama, tetapi di cache prosesor lain. Sekarang utas A menambahkan 1 ke nilai hitungan, dan utas B melakukan hal yang sama. Sekarang variabel telah ditingkatkan dua kali - secara terpisah dengan +1 di cache setiap prosesor.
Jika peningkatan ini dilakukan secara berurutan, variabel hitungan akan digandakan dan ditulis kembali ke memori utama (nilai asli + 2).
Namun, dua peningkatan dilakukan pada saat yang sama tanpa sinkronisasi yang tepat. Terlepas dari utas mana (A atau B) yang menulis versi penghitungan yang diperbarui ke memori utama, nilai baru hanya akan menjadi 1 lebih banyak dari nilai aslinya, meskipun ada dua peningkatan.
Diagram ini mengilustrasikan terjadinya masalah kondisi balapan yang dijelaskan di atas:
Untuk mengatasi masalah ini, Anda dapat menggunakan blok sinkronisasi Java. Blok yang disinkronkan memastikan bahwa hanya satu utas yang dapat memasuki bagian penting kode tertentu pada waktu tertentu.
Blok yang disinkronkan juga menjamin bahwa semua variabel yang diakses di dalam blok yang disinkronkan akan dibaca dari memori utama, dan ketika utas keluar dari blok yang disinkronkan, semua variabel yang diperbarui akan dibuang kembali ke memori utama, terlepas dari apakah variabel tersebut dinyatakan volatile atau No.
GO TO FULL VERSION