Seni bina perkakasan memori
Seni bina perkakasan memori moden berbeza daripada model memori dalaman Java. Oleh itu, anda perlu memahami seni bina perkakasan untuk mengetahui cara model Java berfungsi dengannya. Bahagian ini menerangkan seni bina perkakasan memori umum, dan bahagian seterusnya menerangkan cara Java berfungsi dengannya.
Berikut ialah rajah ringkas seni bina perkakasan komputer moden:
Dalam dunia moden, komputer mempunyai 2 atau lebih pemproses dan ini sudah menjadi kebiasaan. Sesetengah pemproses ini juga mungkin mempunyai berbilang teras. Pada komputer sedemikian, adalah mungkin untuk menjalankan berbilang benang pada masa yang sama. Setiap teras pemproses mampu melaksanakan satu utas pada bila-bila masa. Ini bermakna bahawa mana-mana aplikasi Java ialah apriori berbilang benang, dan dalam program anda, satu utas bagi setiap teras pemproses boleh dijalankan pada satu masa.
Teras pemproses mengandungi satu set daftar yang berada dalam ingatannya (di dalam teras). Ia menjalankan operasi pada data daftar lebih cepat daripada data yang berada dalam memori utama komputer (RAM). Ini kerana pemproses boleh mengakses daftar ini dengan lebih pantas.
Setiap CPU juga boleh mempunyai lapisan cache sendiri. Kebanyakan pemproses moden memilikinya. Pemproses boleh mengakses cachenya lebih cepat daripada memori utama, tetapi tidak secepat daftar dalamannya. Nilai kelajuan capaian cache adalah lebih kurang antara kelajuan capaian memori utama dan daftar dalaman.
Selain itu, pemproses mempunyai tempat untuk mempunyai cache berbilang peringkat. Tetapi ini tidak begitu penting untuk diketahui untuk memahami bagaimana model memori Java berinteraksi dengan memori perkakasan. Adalah penting untuk mengetahui bahawa pemproses mungkin mempunyai beberapa tahap cache.
Mana-mana komputer juga mengandungi RAM (kawasan memori utama) dengan cara yang sama. Semua teras boleh mengakses memori utama. Kawasan memori utama biasanya jauh lebih besar daripada memori cache teras pemproses.
Pada masa ini apabila pemproses memerlukan akses kepada memori utama, ia membaca sebahagian daripadanya ke dalam memori cachenya. Ia juga boleh membaca beberapa data daripada cache ke dalam daftar dalamannya dan kemudian melakukan operasi padanya. Apabila CPU perlu menulis hasil kembali ke memori utama, ia akan membuang data dari daftar dalamannya ke cache, dan pada satu ketika, ke memori utama.
Data yang disimpan dalam cache biasanya dibuang kembali ke memori utama apabila pemproses perlu menyimpan sesuatu yang lain dalam cache. Cache mempunyai keupayaan untuk mengosongkan ingatannya dan menulis data pada masa yang sama. Pemproses tidak perlu membaca atau menulis cache penuh setiap kali semasa kemas kini. Biasanya cache dikemas kini dalam blok kecil memori, ia dipanggil "garis cache". Satu atau lebih "garisan cache" mungkin dibaca ke dalam memori cache, dan satu atau lebih baris cache mungkin dibuang kembali ke memori utama.
Menggabungkan model memori Java dan seni bina perkakasan memori
Seperti yang telah disebutkan, model memori Java dan seni bina perkakasan memori adalah berbeza. Seni bina perkakasan tidak membezakan antara susunan benang dan timbunan. Pada perkakasan, tindanan benang dan HEAP (timbunan) berada dalam ingatan utama.
Bahagian tindanan dan timbunan benang kadangkala mungkin terdapat dalam cache dan daftar dalaman CPU. Ini ditunjukkan dalam rajah:
Apabila objek dan pembolehubah boleh disimpan di kawasan memori komputer yang berbeza, masalah tertentu boleh timbul. Berikut adalah dua yang utama:
- Keterlihatan perubahan yang telah dibuat oleh urutan pada pembolehubah kongsi.
- Keadaan perlumbaan semasa membaca, menyemak dan menulis pembolehubah yang dikongsi.
Kedua-dua isu ini akan diterangkan di bawah.
Keterlihatan Objek Dikongsi
Jika dua atau lebih utas berkongsi objek tanpa menggunakan pengisytiharan atau penyegerakan yang tidak menentu yang betul, maka perubahan pada objek kongsi yang dibuat oleh satu utas mungkin tidak dapat dilihat oleh utas lain.
Bayangkan objek yang dikongsi pada mulanya disimpan dalam ingatan utama. Benang yang berjalan pada CPU membaca objek yang dikongsi ke dalam cache CPU yang sama. Di sana dia membuat perubahan pada objek. Sehingga cache CPU telah dibuang ke memori utama, versi diubah suai bagi objek kongsi tidak kelihatan kepada benang yang berjalan pada CPU lain. Oleh itu, setiap benang boleh mendapatkan salinan objek yang dikongsi sendiri, setiap salinan akan berada dalam cache CPU yang berasingan.
Rajah berikut menggambarkan garis besar keadaan ini. Satu utas yang berjalan di CPU kiri menyalin objek yang dikongsi ke dalam cachenya dan menukar nilai kiraan kepada 2. Perubahan ini tidak dapat dilihat oleh utas lain yang berjalan pada CPU kanan kerana kemas kini untuk mengira masih belum dialihkan kembali ke memori utama.
Untuk menyelesaikan masalah ini, anda boleh menggunakan kata kunci yang tidak menentu semasa mengisytiharkan pembolehubah. Ia boleh memastikan bahawa pembolehubah yang diberikan dibaca terus dari memori utama dan sentiasa ditulis kembali ke memori utama apabila dikemas kini.
Keadaan bangsa
Jika dua atau lebih utas berkongsi objek yang sama dan lebih daripada satu utas mengemas kini pembolehubah dalam objek kongsi itu, maka keadaan perlumbaan mungkin berlaku.
Bayangkan bahawa benang A membaca pembolehubah kiraan objek yang dikongsi ke dalam cache pemprosesnya. Bayangkan juga bahawa benang B melakukan perkara yang sama, tetapi dalam cache pemproses lain. Sekarang benang A menambah 1 pada nilai kiraan, dan benang B melakukan perkara yang sama. Kini pembolehubah telah ditingkatkan dua kali - secara berasingan dengan +1 dalam cache setiap pemproses.
Jika kenaikan ini dilakukan secara berurutan, pembolehubah kiraan akan digandakan dan ditulis kembali ke memori utama (nilai asal + 2).
Walau bagaimanapun, dua kenaikan dilakukan pada masa yang sama tanpa penyegerakan yang betul. Tidak kira benang mana (A atau B) menulis versi kiraan yang dikemas kini ke memori utama, nilai baharu hanya akan 1 lebih daripada nilai asal, walaupun terdapat dua kenaikan.
Rajah ini menggambarkan berlakunya masalah keadaan perlumbaan yang diterangkan di atas:
Untuk menyelesaikan masalah ini, anda boleh menggunakan blok disegerakkan Java. Blok yang disegerakkan memastikan bahawa hanya satu utas boleh memasuki bahagian kritikal kod yang diberikan pada bila-bila masa.
Blok disegerakkan juga menjamin bahawa semua pembolehubah yang diakses di dalam blok disegerakkan akan dibaca daripada memori utama, dan apabila utas keluar dari blok disegerakkan, semua pembolehubah yang dikemas kini akan dipadamkan kembali ke memori utama, tidak kira sama ada pembolehubah itu diisytiharkan tidak menentu atau Tidak.
GO TO FULL VERSION