1. Analisis kode sumber kelas yang tidak aman
Kelas yang tidak aman dalam paket RT.Jar JDK menyediakan operasi atom tingkat perangkat keras. Metode yang tidak aman adalah semua metode asli, dan pustaka implementasi C ++ lokal diakses dengan menggunakan JNI.
Penjelasan Fungsi Utama Kelas Tidak Aman di Rt.Jar. Kelas yang tidak aman menyediakan operasi atom tingkat perangkat keras dan dapat dengan aman mengoperasikan variabel memori secara langsung. Ini banyak digunakan dalam kode sumber JUC. Memahami prinsip -prinsipnya meletakkan dasar untuk mempelajari kode sumber JUC.
Pertama, mari kita pahami penggunaan metode utama di kelas yang tidak aman, sebagai berikut:
1. Metode ObjectFieldOffset (bidang bidang): Mengembalikan alamat offset memori dari variabel yang ditentukan di kelas yang menjadi miliknya. Alamat offset hanya digunakan saat mengakses bidang yang ditentukan dalam fungsi yang tidak aman. Kode berikut menggunakan tidak aman untuk mendapatkan offset memori dari nilai variabel dalam atomiclong dalam objek atomiclong. Kodenya adalah sebagai berikut:
static {try {valueOffset = uncafe.objectFieldOffset (atomiclong.class.getDecLaredField ("value")); } catch (Exception ex) {throw new error (ex); }}2.Int ArrayBaseOffset (Class ArrayClass) Metode: Dapatkan alamat elemen pertama dalam array
3. Metode ArrayIndexScale (Class ArrayClass): Dapatkan jumlah byte yang ditempati oleh satu elemen dalam array
3. Boolean CompareandsWaplong (Object Obj, Long Offset, Long Expect, Long Update) Metode: Bandingkan apakah nilai variabel offset offset di objek OBJ sama dengan yang diharapkan. Jika sama, itu diperbarui dengan nilai pembaruan, dan kemudian mengembalikan true, jika tidak, ia akan mengembalikan false.
4. Metode GetLongvolative (Object Obj, Offset Long Obj, Long Offset): Dapatkan nilai semantik memori yang mudah menguap sesuai dengan variabel offset offset di objek OBJ.
5. Void putorderedlong (objek OBJ, offset panjang, nilai panjang) Metode: Tetapkan nilai bidang panjang yang sesuai dengan alamat offset offset dalam objek OBJ ke nilai. Ini adalah metode putlongvolatile dengan penundaan, dan tidak menjamin bahwa modifikasi nilai akan segera terlihat oleh utas lain. Variabel hanya berguna jika dimodifikasi dengan volatil dan diharapkan dimodifikasi secara tidak terduga.
6. Void Park (Boolean Isabsolute, Long Time) Metode: Blokir utas saat ini. Ketika parameter isabsolute sama dengan false, waktu yang sama dengan 0 berarti memblokir sepanjang waktu. Waktu lebih besar dari 0 berarti bahwa utas pemblokiran akan dibangunkan setelah menunggu waktu yang ditentukan. Waktu ini adalah nilai relatif, nilai tambahan, yaitu utas saat ini akan dibangunkan setelah mengumpulkan waktu relatif terhadap waktu saat ini. Jika Isabsolute sama dengan True dan waktu lebih besar dari 0, itu berarti bahwa itu akan terbangun setelah diblokir ke titik waktu yang ditentukan. Di sini waktu adalah waktu absolut, yang merupakan nilai yang dikonversi ke MS pada titik waktu tertentu. Selain itu, ketika utas lain memanggil metode interupsi dari utas pemblokiran saat ini dan mengganggu utas saat ini, utas saat ini juga akan kembali. Ketika utas lain memanggil metode unpark dan mengambil utas saat ini sebagai parameter, utas saat ini juga akan kembali.
7. Void Unpark (Object Thread) Metode: Bangun utas pemblokiran setelah menelepon taman, dan parameternya adalah utas yang perlu dibangunkan.
Beberapa metode baru telah ditambahkan ke JDK1.8. Berikut adalah daftar sederhana metode untuk mengoperasikan jenis panjang sebagai berikut:
8. Metode GetAndSetLong (Object Obj, Long Offset, Pembaruan Panjang): Dapatkan nilai semantik volatil variabel dengan offset di objek OBJ, dan atur nilai semantik volatil variabel untuk diperbarui. Metode penggunaan adalah sebagai berikut:
final publik long getAndSetLong (objek obj, offset panjang, pembaruan panjang) {long l; do {l = getLongVolatile (obj, offset); // (1)} while (! compareeAndswaplong (obj, offset, l, update)); kembali l; }Dari kode internal (1), Anda dapat menggunakan getLongVolative untuk mendapatkan nilai variabel saat ini, dan kemudian menggunakan operasi atom CAS untuk mengatur nilai baru. Di sini, menggunakan while loop memperhitungkan situasi di mana beberapa utas menelepon pada saat yang sama, dan kemudian spin-retry diperlukan setelah CAS gagal.
9. Metode GetAnddllong (Object Obj, Long Offset, Long AddValue): Dapatkan nilai semantik variabel volatile dengan offset di objek OBJ, dan atur nilai variabel ke nilai asli + addValue. Metode penggunaan adalah sebagai berikut:
final publik long getAndDlong (objek obj, offset panjang, addValue panjang) {long l; do {l = getLongVolatile (obj, offset); } while (! compareEndsWaplong (obj, offset, l, l + addValue)); kembali l; }Mirip dengan implementasi getAndSetLong, kecuali bahwa ketika menggunakan CAS di sini, nilai asli + nilai addValue parameter tambahan yang ditransfer digunakan.
Jadi bagaimana cara menggunakan kelas yang tidak aman?
Melihat bahwa tidak aman itu sangat luar biasa, apakah Anda benar -benar ingin berlatih? Oke, mari kita lihat kode berikut pertama:
Paket com.hjc; import sun.misc.unsafe;/*** Dibuat oleh Cong pada 2018/6/6. */kelas publik testunsafe {// Dapatkan instance dari tidak aman (2.2.1) statis final tidak aman tidak aman = uncafe.getunsafe (); // Catat nilai offset status variabel di kelas testunsafe (2.2.2) statis statis long stateOffset; // variabel (2.2.3) status panjang volatile pribadi = 0; static {try {// Dapatkan nilai offset dari variabel negara di kelas testunsafe (2.2.4) stateOffset = unsafe.objectFieldOffset (testunsafe.class.getDeclaredfield ("state")); } catch (Exception ex) {System.out.println (ex.getLocalizedMessage ()); Lempar Kesalahan Baru (EX); }} public static void main (string [] args) {// Buat instance dan atur nilai status ke 1 (2.2.5) testunsafe test = testunsafe baru (); //(2.2.6) Boolean Sucess = unsafe.ComeEndsWapint (Test, StateOffset, 0, 1); System.out.println (Sucess); }}Kode (2.2.1) mendapat instance yang tidak aman, dan kode (2.2.3) membuat keadaan variabel yang diinisialisasi ke 0.
Kode (2.2.4) menggunakan tidak aman.ObjectFieldOffset untuk mendapatkan alamat offset memori dari variabel negara di kelas testunsafe di objek testunsafe dan menyimpannya ke variabel stateOffset.
Kode (2.2.6) memanggil metode CompareANDSWAPINT dari instance yang tidak aman yang dibuat dan menetapkan nilai variabel negara dari objek uji. Secara khusus, jika variabel status offset memori dari objek uji adalah stateOffset adalah 0, nilai pembaruan diubah menjadi 1.
Kami ingin memasukkan true dalam kode di atas, tetapi setelah eksekusi, hasil berikut akan menjadi output:
Mengapa ini terjadi? Anda harus memasukkan kode getunsafe, seperti lihat apa yang dilakukan di dalamnya:
final private statis tidak aman theunsafe = baru tidak aman (); public static tidak aman getunsafe () {// (2.2.7) class localclass = reflection.getCallerClass (); // (2.2.8) if (! Vm.issystemdomainloader (localclass.getClassLoader ())) {throw new SecurityException ("Uncafe"); } return theunsafe;} // menilai apakah paramClassLoader adalah bootstrap class loader (2.2.9) public static boolean issystemdomainloader (classloader paramclassloader) {return paramclassloader == null; }Kode (2.2.7) Mendapat objek kelas dari objek yang disebut GetunSafe, di sini adalah testunsafe.cals.
Kode (2.2.8) menentukan apakah itu adalah localclass yang dimuat oleh bootstrap class loader. Kuncinya di sini adalah apakah bootstrap loader memuat testunsafe.class. Mereka yang telah melihat mekanisme pemuatan kelas mesin virtual Java dengan jelas melihat bahwa itu karena testunsafe.class dimuat menggunakan appclassloader, sehingga pengecualian dilemparkan langsung ke sini.
Jadi pertanyaannya adalah, mengapa Anda perlu membuat penilaian ini?
Kita tahu bahwa kelas yang tidak aman disediakan di RT.Jar, dan kelas di Rt.jar dimuat menggunakan bootstrap class loader. Kelas tempat kami memulai fungsi utama dimuat menggunakan AppClassLoader, jadi ketika memuat kelas yang tidak aman di fungsi utama, mengingat bahwa mekanisme delegasi induk akan mendelegasikan ke bootstrap untuk memuat kelas yang tidak aman.
Jika tidak ada otentikasi kode (2.2.8), maka aplikasi kami dapat menggunakan tidak aman untuk melakukan sesuatu sesuka hati. Kelas yang tidak aman dapat mengoperasikan memori secara langsung, yang sangat tidak aman. Oleh karena itu, tim pengembangan JDK secara khusus membuat pembatasan ini, tidak memungkinkan pengembang untuk menggunakan kelas yang tidak aman di bawah saluran reguler, tetapi menggunakan fungsi yang tidak aman di kelas inti di RT.Jar.
Pertanyaannya adalah, apa yang harus kita lakukan jika kita benar -benar ingin instantiate kelas yang tidak aman dan menggunakan fungsi yang tidak aman?
Kita tidak boleh melupakan teknologi hitam refleksi, dan menggunakan refleksi universal untuk mendapatkan metode instance yang tidak aman. Kodenya adalah sebagai berikut:
Paket com.hjc; import sun.misc.unsafe; import java.lang.reflect.field;/*** Dibuat oleh Cong pada 2018/6/6. */kelas publik testunsafe {static final tidak aman tidak aman; stategset panjang terakhir statis; status panjang volatile pribadi = 0; static {try {// renungan untuk mendapatkan variabel anggota Theunsafe theunsafe (2.2.10) bidang = uncafe.class.getDeclaredfield ("theUnsafe"); // diatur ke field yang dapat diakses (2.2.11 ).setAccessible (true); // Dapatkan nilai variabel ini (2.2.12) tidak aman = (tidak aman) bidang.get (null); // Dapatkan offset status di testunsafe (2.2.13) stateOffset = uncafe.objectfieldOffset (testunsafe.class.getDeclaredfield ("state")); } catch (Exception ex) {System.out.println (ex.getLocalizedMessage ()); Lempar Kesalahan Baru (EX); }} public static void main (string [] args) {testunsafe test = new testunsafe (); Boolean Success = uncafe.comppareandswapint (tes, stateoffset, 0, 1); System.out.println (Sucess); }}Jika kode di atas (2.2.10 2.2.11 2.2.12) mencerminkan contoh yang tidak aman, hasil berjalan adalah sebagai berikut:
2. Penelitian tentang Kode Sumber Kelas Locksupport
Locksupport di RT.Jar di JDK adalah kelas alat, dan fungsi utamanya adalah untuk menangguhkan dan membangunkan utas. Ini adalah dasar untuk membuat kunci dan kelas sinkronisasi lainnya.
Kelas Locksupport akan dikaitkan dengan setiap utas yang menggunakannya. Utas yang memanggil metode kelas Locksupport secara default tidak memiliki lisensi. Locksupport diimplementasikan secara internal menggunakan kelas yang tidak aman.
Di sini kita harus memperhatikan beberapa fungsi penting dari Locksupport, sebagai berikut:
1.void Park () Metode: Jika Park Panggilan Thread () telah memperoleh lisensi yang terkait dengan Locksupport, maka memanggil Locksupport.park () akan segera kembali. Kalau tidak, utas panggilan akan dilarang berpartisipasi dalam penjadwalan utas, yaitu, itu akan diblokir dan ditangguhkan. Kode berikut adalah contoh berikut:
Paket com.hjc; import java.util.concurrent.locks.locksupport;/*** Dibuat oleh Cong pada 2018/6/6. */kelas publik LockSupportTest {public static void main (string [] args) {System.out.println ("Park Start!"); Locksupport.park (); System.out.println ("Park Stop!"); }}Seperti yang ditunjukkan pada kode di atas, langsung panggil metode taman di fungsi utama, dan hasil akhirnya hanya akan menghasilkan mulai taman! Maka utas saat ini akan ditangguhkan, karena utas panggilan tidak memiliki lisensi secara default. Hasil operasi adalah sebagai berikut:
Ketika Anda melihat utas lain memanggil metode Unpark (Thread Thread) dan utas saat ini digunakan sebagai parameter, utas yang memanggil metode taman akan kembali. Selain itu, utas lain memanggil metode interrupt () dari utas pemblokiran. Ketika bendera interupsi diatur atau utas pemblokiran akan kembali setelah bangun false utas, yang terbaik adalah menggunakan kondisi loop untuk menilai.
Perlu dicatat bahwa utas yang memanggil metode park () yang diblokir terganggu oleh utas lain dan utas yang diblokir kembali, tidak akan melempar pengecualian Exception interrupted.
2.Void Unpark (Thread Thread) Metode Ketika utas memanggil unpark, jika utas parameter utas tidak menahan lisensi yang terkait dengan utas dan kelas Locksupport, biarkan utas menahannya. Jika utas yang disebut park () ditangguhkan sebelumnya dan utas dipanggil, utas akan dibangunkan setelah unpark dipanggil.
Jika utas belum menelepon Park sebelumnya, setelah memanggil metode Unpark, metode Park () akan segera dikembalikan. Kode di atas dimodifikasi sebagai berikut:
Paket com.hjc; import java.util.concurrent.locks.locksupport;/*** Dibuat oleh Cong pada 2018/6/6. */kelas publik LockSupportTest {public static void main (string [] args) {System.out.println ("Park Start!"); // Buat utas saat ini dapatkan lisensi locksupport.unpark (thread.currentThread ()); // hubungi taman locksupport.park () lagi; System.out.println ("Park Stop!"); }}Hasil operasi adalah sebagai berikut:
Selanjutnya, kami melihat contoh untuk memperdalam pemahaman kami tentang taman, membuka tanda, kodenya adalah sebagai berikut:
impor java.util.concurrent.locks.locksupport;/*** Dibuat oleh Cong pada 2018/6/6. */Kelas Publik LocksupportTest {public static void main (string [] args) melempar interruptedException {thread thread = utas baru (runnable baru () {@Override public void run () {System.out.println ("Child Thread Park Start!");/Call The Park Metode dan gantung diri Anda sendiri locksupport.park () () () System. }}); // Mulailah utas thread anak.start (); // utas utama menampung thread 1s.sleep (1000); System.out.println ("Utas utama unpark start!"); // Hubungi Unpark untuk membiarkan utas menahan lisensi, dan kemudian metode taman akan mengembalikan locksupport.unpark (utas); }}Hasil operasi adalah sebagai berikut:
Kode di atas pertama kali membuat utas utas anak. Setelah startup, utas anak memanggil metode taman. Karena utas anak default tidak memiliki lisensi, itu akan menggantung sendiri.
Benang utama tidur selama 1s. Tujuannya adalah bahwa utas utama memanggil metode unpark dan memungkinkan utas anak menghasilkan taman utas anak dimulai! dan blokir.
Utas utama kemudian menjalankan metode unpark, dengan parameter menjadi utas anak, tujuannya adalah untuk membiarkan utas anak memegang lisensi, dan kemudian metode taman yang dipanggil oleh utas anak kembali.
Saat mengembalikan metode taman, itu tidak akan memberi tahu Anda apa alasannya kembali. Oleh karena itu, penelepon perlu memeriksa lagi apakah kondisinya terpenuhi berdasarkan metode taman apa yang dia lakukan dalam panggilan saat ini. Jika tidak terpenuhi, ia perlu memanggil metode taman lagi.
Misalnya, keadaan interupsi utas saat kembali, dapat ditentukan apakah itu kembali karena terganggu berdasarkan perbandingan keadaan interupsi sebelum dan sesudah panggilan.
Untuk mengilustrasikan bahwa utas setelah memanggil metode taman akan kembali setelah terganggu, modifikasi contoh kode di atas dan hapus locksupport.unpark (thread); dan kemudian tambahkan thread.interrupt (); kodenya adalah sebagai berikut:
impor java.util.concurrent.locks.locksupport;/*** Dibuat oleh Cong pada 2018/6/6. */kelas publik LocksupportTest {public static void main (string [] args) melempar interruptedException {thread thread = thread baru (runnable baru () {@Override public void run () {System.out.println ("Subthread Park Start!"); // Panggil metode taman dan hang sendiri. (! // Mulai thread thread anak.start (); // utas utama menampung thread 1s.sleep (1000); System.out.println ("Utas utama unpark start!"); // mengganggu utas utas anak.Interrupt (); }}Hasil operasi adalah sebagai berikut:
Sebagai kode di atas, utas anak hanya akan berakhir setelah utas anak terganggu. Jika utas anak tidak terganggu, bahkan jika Anda memanggil Unpark (utas) utas anak tidak akan berakhir.
3. Void Parknanos (Long Nanos) Metode: Mirip dengan Park, jika Park Panggilan Thread telah memperoleh lisensi yang terkait dengan LockSupport, kemudian memanggil Locksupport.park () akan segera kembali. Perbedaannya adalah bahwa jika utas yang memanggil utas tidak diperoleh, itu akan ditangguhkan dan kemudian kembali setelah waktu nano.
Park juga mendukung tiga metode dengan parameter blocker. Ketika sebuah utas memanggil taman tanpa memegang lisensi dan diblokir dan ditangguhkan, objek blocker akan direkam di dalam utas.
Gunakan alat diagnostik untuk mengamati alasan mengapa utas diblokir. Alat diagnostik menggunakan metode getBlocker (utas) untuk mendapatkan objek blocker. Oleh karena itu, JDK merekomendasikan agar kami menggunakan metode taman dengan parameter blocker dan mengatur blocker untuk ini, jadi ketika pembuangan memori memecahkan masalah masalah, kita dapat mengetahui kelas mana yang diblokir.
Contohnya adalah sebagai berikut:
impor java.util.concurrent.locks.locksupport;/*** Dibuat oleh Cong pada 2018/6/6. */Public Class TestPark {public void testPark () {locksupport.park (); // (1)} public static void main (string [] args) {testPark testPark = testPark baru (); testpark.testpark (); }}Hasil operasi adalah sebagai berikut:
Anda dapat melihat bahwa itu sedang berjalan memblokir, jadi kami perlu menggunakan alat di direktori JDK/bin untuk melihatnya. Jika Anda tidak tahu, disarankan untuk melihat alat pemantauan JVM terlebih dahulu.
Saat menggunakan JStack PID untuk melihat tumpukan utas setelah berjalan, hasil yang dapat Anda lihat adalah sebagai berikut:
Kemudian kami memodifikasi kode di atas (1) sebagai berikut:
Locksupport.park (ini); // (1)
Jalankan lagi dan gunakan JStack PID untuk melihat hasilnya sebagai berikut:
Dapat dilihat bahwa setelah metode taman dengan blocker, tumpukan utas dapat memberikan lebih banyak informasi tentang pemblokiran objek.
Kemudian kami akan memeriksa kode sumber fungsi taman (objek blocker), kode sumber adalah sebagai berikut:
public static void park (objek blocker) {// Dapatkan utas panggilan thread t = thread.currentThread (); // atur setBlocker variabel blocker (t, blocker); // gantung utas tidak aman.park (false, 0l); // Bersihkan variabel blocker setelah utas diaktifkan, karena alasannya biasanya dianalisis ketika utas diblokir setBlocker (t, null);}Ada variabel dalam objek volatile kelas utas ParkBlocker. Ini digunakan untuk menyimpan objek blocker yang dilewati oleh taman, yaitu, variabel blocker disimpan dalam variabel anggota dari utas yang memanggil metode taman.
4. Fungsi Void Parknanos (Object Blocker, Long Nanos) memiliki waktu batas waktu tambahan dibandingkan dengan Park (Object Blocker).
5. Void Parkuntil (Object Blocker, Batas Waktu Panjang) Kode Sumber Parkuntil adalah sebagai berikut:
public static void parkuntil (objek blocker, batas waktu panjang) {thread t = thread.currentThread (); setBlocker (t, blocker); // isabsolute = true, time = tenggat waktu; berarti bahwa tidak aman.park (benar, tenggat waktu); setBlocker (T, null); }Anda dapat melihat bahwa ini adalah tenggat waktu yang ditetapkan, unit waktu adalah milidetik, yang dikonversi menjadi nilai setelah milidetik dari tahun 1970 ke titik waktu saat ini. Perbedaan antara ini dan parknanos (objek blocker, nano panjang) adalah bahwa yang terakhir menghitung waktu nano menunggu dari waktu saat ini, sedangkan yang pertama menentukan titik waktu.
Misalnya, kita perlu menunggu hingga 20:34 di 2018.06.06, dan kemudian mengubah titik waktu ini menjadi jumlah total milidetik dari tahun 1970 ke titik waktu ini.
Mari kita lihat contoh lain, kodenya adalah sebagai berikut:
Impor java.util.queue; impor java.util.concurrent.concurrentlinkedqueue; impor java.util.concurrent.atomic.atomicboolean; impor java.util.concurrent.locks.locksupport;/*** dibuat oleh CONG pada 2018/6/6/6. */kelas publik FIFOMUTEX {private final atomicboolean terkunci = atomicboolean baru (false); antrian akhir pribadi <tread> pelayan = concurrentlinkedqueue baru <tread> (); public void lock () {boolean terputus = false; Thread current = thread.currentThread (); waiters.add (saat ini); // Hanya utas kepala tim yang dapat memperoleh kunci (1) while (waiters.peek ()! = Saat ini ||! Terkunci.ComeeAndset (false, true)) {locksupport.park (ini); if (thread.interrupted ()) // (2) terputus = true; } waiters.remove (); if (was intrupted) // (3) current.interrupt (); } public void unlock () {locked.set (false); Locksupport.unpark (waiters.peek ()); }}Anda dapat melihat bahwa ini adalah kunci pertama di-pertama, yaitu, hanya elemen header antrian yang dapat memperolehnya. Kode (1) Jika utas saat ini bukan header antrian atau kunci saat ini telah diperoleh oleh utas lain, maka hubungi metode taman untuk menangguhkan dirinya sendiri.
Kemudian kode (2) membuat penilaian. Jika metode taman kembali karena terganggu, interupsi diabaikan, dan bendera interupsi diatur ulang, dan hanya bendera yang dibuat, dan kemudian menentukan apakah utas saat ini adalah elemen kepala antrian atau apakah kunci telah diperoleh oleh utas lain. Jika demikian, terus hubungi metode taman untuk menggantung diri.
Maka jika tanda benar dalam kode (3), utas akan terganggu. Bagaimana Anda memahami ini? Bahkan, utas lain mengganggu utas. Meskipun saya tidak tertarik pada sinyal interupsi dan mengabaikannya, itu tidak berarti bahwa utas lain tidak tertarik pada bendera, jadi saya perlu mengembalikannya.
Meringkaskan
Di atas adalah seluruh konten artikel ini. Saya berharap konten artikel ini memiliki nilai referensi tertentu untuk studi atau pekerjaan semua orang. Jika Anda memiliki pertanyaan, Anda dapat meninggalkan pesan untuk berkomunikasi. Terima kasih atas dukungan Anda ke wulin.com.