Ketika mencoba memahami bagaimana SubmissionPublisher
( kode sumber di Java SE 10, OpenJDK | docs ), sebuah kelas baru yang ditambahkan ke Java SE dalam versi 9, telah diterapkan, saya menemukan beberapa panggilan API yang VarHandle
sebelumnya tidak saya sadari:
fullFence
, acquireFence
, releaseFence
, loadLoadFence
Dan storeStoreFence
.
Setelah melakukan beberapa penelitian, terutama mengenai konsep hambatan / pagar ingatan (saya pernah mendengar tentang mereka sebelumnya, ya; tetapi tidak pernah menggunakannya, sehingga cukup tidak terbiasa dengan semantik mereka), saya pikir saya memiliki pemahaman dasar tentang apa itu untuk . Meskipun demikian, karena pertanyaan saya mungkin timbul dari kesalahpahaman, saya ingin memastikan bahwa saya telah melakukannya dengan benar:
Hambatan ingatan adalah kendala menata ulang tentang operasi membaca dan menulis.
Hambatan memori dapat dikategorikan ke dalam dua kategori utama: hambatan memori searah dan dua arah, tergantung pada apakah mereka menetapkan batasan baik membaca atau menulis atau keduanya.
C ++ mendukung berbagai hambatan memori , namun, ini tidak cocok dengan yang disediakan oleh
VarHandle
. Namun, beberapa hambatan memori yang tersedia dalamVarHandle
memberikan efek pemesanan yang kompatibel dengan hambatan memori C ++ yang sesuai.#fullFence
kompatibel untukatomic_thread_fence(memory_order_seq_cst)
#acquireFence
kompatibel untukatomic_thread_fence(memory_order_acquire)
#releaseFence
kompatibel untukatomic_thread_fence(memory_order_release)
#loadLoadFence
dan#storeStoreFence
tidak memiliki bagian penghitung C ++ yang kompatibel
Kata yang kompatibel tampaknya sangat penting di sini karena semantiknya jelas berbeda dalam hal perincian. Sebagai contoh, semua hambatan C ++ adalah dua arah, sedangkan hambatan Java tidak (harus).
- Sebagian besar hambatan memori juga memiliki efek sinkronisasi. Itu terutama tergantung pada jenis penghalang yang digunakan dan instruksi penghalang yang sebelumnya dieksekusi di utas lainnya. Karena implikasi penuh dari instruksi penghalang adalah spesifik untuk perangkat keras, saya akan tetap menggunakan penghalang level lebih tinggi (C ++). Dalam C ++, misalnya, perubahan yang dibuat sebelum instruksi barrier rilis terlihat oleh thread yang menjalankan instruksi barrier memperoleh .
Apakah asumsi saya benar? Jika demikian, pertanyaan saya yang muncul adalah:
Apakah hambatan memori tersedia dalam
VarHandle
penyebab sinkronisasi jenis apa pun?Terlepas dari apakah mereka menyebabkan sinkronisasi memori atau tidak, kendala pemesanan ulang apa yang berguna untuk Java? Java Memory Model sudah memberikan jaminan yang sangat kuat terkait pemesanan ketika bidang yang mudah berubah, kunci atau
VarHandle
operasi seperti#compareAndSet
terlibat.
Jika Anda mencari contoh: Yang disebutkan di atas BufferedSubscription
, kelas dalam dari SubmissionPublisher
(sumber terkait di atas), membuat pagar penuh di baris 1079 (berfungsi growAndAdd
; karena situs web yang ditautkan tidak mendukung pengidentifikasi fragmen, hanya CTRL + F untuk itu ). Namun, tidak jelas bagi saya untuk apa itu.
plain -> opaque -> release/acquire -> volatile (sequential consistency)
.Jawaban:
Ini terutama bukan jawaban, sungguh (awalnya ingin memberikan komentar, tetapi seperti yang Anda lihat, ini terlalu lama). Hanya saja saya sering mempertanyakan ini sendiri, melakukan banyak membaca dan meneliti dan pada saat ini saya dapat dengan aman mengatakan: ini rumit. Saya bahkan menulis beberapa tes dengan jcstress untuk mengetahui bagaimana sebenarnya mereka bekerja (sambil melihat kode assembly yang dihasilkan) dan sementara beberapa dari mereka entah bagaimana masuk akal, subjek secara umum tidak berarti mudah.
Hal pertama yang perlu Anda pahami:
Ini sedang dalam proses.
Kedua, jika Anda benar-benar ingin menggaruk permukaan di sini, ini adalah hal pertama yang harus diperhatikan . Pembicaraan itu luar biasa. Bagian favorit saya adalah ketika Herb Sutter mengangkat 5 jarinya dan berkata, "Ini adalah berapa banyak orang yang dapat bekerja dengan benar dan benar dengan ini." Itu akan memberi Anda sedikit kerumitan yang terlibat. Namun demikian, ada beberapa contoh sepele yang mudah dipahami (seperti penghitung yang diperbarui oleh banyak utas yang tidak peduli dengan jaminan memori lainnya , tetapi hanya peduli bahwa itu sendiri akan bertambah dengan benar).
Contoh lain adalah ketika (dalam java) Anda ingin
volatile
bendera untuk mengontrol utas untuk berhenti / mulai. Anda tahu, klasik:Jika Anda bekerja dengan java, Anda akan tahu bahwa tanpa
volatile
kode ini rusak (Anda dapat membaca mengapa penguncian cek ganda rusak tanpa itu misalnya). Tetapi apakah Anda juga tahu bahwa bagi sebagian orang yang menulis kode kinerja tinggi ini terlalu banyak?volatile
baca / tulis juga menjamin konsistensi berurutan - yang memiliki beberapa jaminan kuat dan beberapa orang menginginkan versi yang lebih lemah ini.Dan Anda akan mempertanyakan mengapa seseorang mungkin membutuhkannya misalnya? Tidak semua orang tertarik dengan semua perubahan yang didukung oleh babi
volatile
.Mari kita lihat bagaimana kita mencapai ini di java. Pertama-tama, seperti eksotis hal yang sudah ada di API:
AtomicInteger::lazySet
. Ini tidak ditentukan dalam Java Memory Model dan tidak memiliki definisi yang jelas ; masih orang menggunakannya (LMAX, afaik atau ini untuk membaca lebih lanjut ). IMHO,AtomicInteger::lazySet
adalahVarHandle::releaseFence
(atauVarHandle::storeStoreFence
).Mari kita coba jawab mengapa seseorang membutuhkan ini ?
JMM pada dasarnya memiliki dua cara untuk mengakses bidang: polos dan volatil (yang menjamin konsistensi berurutan ). Semua metode ini yang Anda sebutkan ada di sana untuk membawa sesuatu di antara keduanya - rilis / dapatkan semantik ; ada kasus, saya kira, di mana orang benar - benar membutuhkan ini.
Bahkan lebih relaksasi dari rilis / memperoleh akan buram , yang saya masih mencoba untuk memahami .
Jadi intinya (pemahaman Anda cukup benar, btw): jika Anda berencana untuk menggunakan ini di java - mereka tidak memiliki spesifikasi saat ini, lakukan dengan risiko Anda sendiri. Jika Anda ingin memahaminya, mode yang setara dengan C ++ adalah tempat untuk memulai.
sumber
lazySet
dengan menghubungkan ke jawaban kuno, dokumentasi saat ini tepatnya mengatakan apa artinya, saat ini. Lebih lanjut, itu menyesatkan untuk mengatakan bahwa JMM hanya memiliki dua mode akses. Kami memiliki read dan volatile write yang volatile , yang bersama-sama dapat membangun hubungan yang terjadi sebelum .volatile
kata kunci adalah C99, lima tahun setelah Java, tetapi masih tidak memiliki semantik yang berguna, bahkan C ++ 03 tidak memiliki Model Memori. Hal-hal yang oleh C ++ disebut "atomik" juga jauh lebih muda dari Jawa. Danvolatile
kata kunci bahkan tidak menyiratkan pembaruan atom. Jadi mengapa harus dinamai demikian.restrict
, namun, saya ingat saat-saat ketika saya harus menulis__volatile
untuk menggunakan ekstensi kompiler non-kata kunci. Jadi mungkin, itu tidak mengimplementasikan C89 sepenuhnya? Jangan bilang saya yang lama. Sebelum Java 5,volatile
jauh lebih dekat dengan C. Tetapi Java tidak memiliki MMIO, jadi tujuannya selalu multi-threading, tetapi semantik pra-Java 5 tidak terlalu berguna untuk itu. Jadi rilis / peroleh seperti semantik ditambahkan, tapi tetap saja itu bukan atomik (pembaruan atom adalah fitur tambahan yang dibangun di atasnya).