Baru -baru ini, saya membaca kode layer kerangka Android dan melihat kelas threadlocal. Saya sedikit asing, jadi saya membaca berbagai blog terkait satu per satu. Saya kemudian mempelajari kode sumber dan menemukan bahwa pemahaman saya berbeda dari posting blog yang saya baca sebelumnya, jadi saya memutuskan untuk menulis artikel untuk berbicara tentang pemahaman saya, berharap itu dapat memainkan peran berikut:
- dapat menghapus hasil penelitian dan memperdalam pemahaman Anda;
- Ini dapat berperan dalam menarik batu giok dan membantu siswa yang tertarik membersihkan ide -ide mereka;
- Bagikan pengalaman belajar Anda dan berkomunikasi dan pelajari dengan semua orang.
1. Apa itu threadlocal
Threadlocal adalah kelas dasar dari Perpustakaan Kelas Java, di bawah paket java.lang;
Penjelasan resmi adalah sebagai berikut:
Menerapkan penyimpanan thread-lokal, yaitu variabel yang masing-masing utas memiliki nilainya sendiri. Semua utas memiliki objek threadlocal yang sama, tetapi masing -masing melihat nilai yang berbeda saat mengaksesnya, dan perubahan yang dibuat oleh satu utas tidak mempengaruhi utas lainnya. Implementasi mendukung nilai nol.
Makna umum adalah:
Mekanisme penyimpanan thread lokal dapat diimplementasikan. Variabel utas adalah variabel yang dapat memiliki nilai yang berbeda di utas yang berbeda. Semua utas dapat berbagi objek threadlocal yang sama, tetapi utas yang berbeda dapat memperoleh nilai yang berbeda saat diakses, dan perubahan utas apa pun tidak akan mempengaruhi utas lain. Implementasi kelas mendukung nilai null (nilai nol dapat dilewati dan diakses dalam metode set dan mendapatkan).
Singkatnya, ada tiga karakteristik:
- Dapatkan nilai yang berbeda saat diakses oleh utas yang berbeda
- Setiap benang perubahan tidak akan mempengaruhi utas lain
- Dukung NULL
Berikut ini adalah contoh dari fitur -fitur ini. Pertama, kami mendefinisikan kelas tes. Di kelas ini, kami memverifikasi tiga fitur yang disebutkan di atas. Definisi kelas adalah sebagai berikut:
Test.java
Uji kelas publik {// Tentukan threadlocal private static threadlocal nama; public static void main (string [] args) melempar pengecualian {name = new ThreadLocal (); // define Thread ATHREAD A = NEW Thread () {public void run () {System.out.println ("Sebelum set, bernilai adalah:"+Name. " A "); System.out.println (" Setelah setel set, nilai adalah: "+name.get ());}}; // Tentukan utas bthread b = thread baru () {public void run () {System.out.println (" After Invoke Set, Nilai adalah: "+name.get ()); name.set (" name B " : "+name.get ());}}; // tidak memanggil set, cetak nilainya adalah nullsystem.out.println (name.get ()); // Invoke Set untuk mengisi valueName.set (" Thread Main "); // Start Thread AA.Start (); a.join (); // Cetak Nilai Nilai Setelah Mengubah Nilai yang Mengubah THE THEAt Thread bb.start (); b.join (); // cetak nilai setelah mengubah nilai dengan thread bsystem.out.println (name.get ())}}Analisis Kode:
Dari definisi kita dapat melihat bahwa hanya satu objek threadlocal yang dinyatakan, dan tiga utas lainnya (utas utama, utas A dan utas B) berbagi objek yang sama; Kemudian, nilai objek dimodifikasi dalam utas yang berbeda dan nilai objek diakses di utas yang berbeda, dan hasilnya adalah output untuk dilihat di konsol.
Lihat hasilnya:
Dari hasil output konsol, Anda dapat melihat bahwa ada tiga output nol di dalamnya. Ini karena objek tidak ditetapkan sebelum output, yang memverifikasi fitur NULL mendukung. Selain itu, dapat ditemukan bahwa di setiap utas, saya telah memodifikasi nilai objek, tetapi ketika utas lain mengakses objek, itu bukan nilai yang dimodifikasi, tetapi nilai thread-lokal; Ini juga memverifikasi dua fitur lainnya.
2. Peran Threadlocal
Semua orang tahu bahwa skenario penggunaannya sebagian besar merupakan pemrograman multi-threaded. Adapun fungsi spesifiknya, bagaimana Anda mengatakan itu? Saya pikir ini hanya dapat didefinisikan secara umum, karena atribut fungsional suatu hal akan membatasi pemikiran semua orang setelah didefinisikan. Misalnya, pisau dapur digunakan untuk memotong sayuran, dan banyak orang tidak akan menggunakannya untuk memotong semangka.
Di sini, saya akan berbicara tentang pemahaman saya tentang fungsinya hanya untuk referensi dan berharap itu akan membantu. Mari kita jelaskan seperti ini. Ketika program multi-threaded perlu merangkum beberapa tugas dari sebagian besar utas (yaitu, beberapa kode dalam metode run), threadlocal dapat digunakan untuk membungkus variabel anggota terkait utas dalam badan enkapsulasi untuk memastikan eksklusivitas akses utas, dan semua utas dapat berbagi objek enkapsulasi; Anda dapat merujuk ke Looper di Android. Pemrogram yang tidak dapat menggambarkan masalah dengan kode bukan programmer yang baik;
Lihatlah kode: Alat untuk menyatakan kode utas yang memakan waktu (dibuat untuk menggambarkan masalah)
Statisticcosttime.java
// kelas yang statistik biaya timepublic class statisticCosttime {// Rekam starttime // thread private threadlocal starttime = new threadlocal (); private long starttime; // costtime private threadlocal = {{{{{{{{{{{{{{{{{{{); InstanceFactory.Instance;} private static class callsFactory {private static statisticCosttime instance = statistik baru baru ();} // startpublic void start () {// startTime.set (System. NanoTime ()); startTime = System.nanoTime ();}// endpublic void (); starttime = systtime.nanoTime ();}// endpublic void (); starttime = systtime.nanoTime ();}// endpublic void (); startTime = nanTtime (nanTtime ();} endpublic end () endpublic end (); startTime.get ()); costtime = system.nanoTime () - startTime;} public long getStTime () {return startTime; // return startTime.get ();} public long getCosttime () {// return costtime.get (); return costtime;}Oke, desain alat selesai, sekarang kami menggunakannya untuk menghitung utas yang memakan waktu dan mencoba:
Main.java
public class Main{public static void main(String[] args) throws Exception{// Define the thread aThread a = new Thread(){public void run(){try{// start record timeStatisticCostTime.shareInstance().start();sleep(200);// print the start time of A System.out.println ("A-StartTime:"+StatisticCostTime.ShareINSTANCE (). GetStarttime ()); // Akhiri RecordStatisticCostTime.ShareINSTANCE (). END (); // Cetak CostTime of a System.out.println ("a:"+statisticTtime. e) {}}}; // Mulai aa.start (); // Tentukan utas bthread b = utas baru () {public void run () {coba {// Rekam waktu mulai b1statisticcosttime.shareInstance (). start (); sleep (100); // cetak waktu mulai ke Consolesystem.out.println ("B1-Starttime:"+StatisticCostTime.ShareInstance (). GetStartTime ()); // Akhir Waktu Mulai Catatan B1statisticCosttime.ShareINSTANCE (). END (); // Cetak waktu biaya dari B1system.out.println ("b1:"+statisticcosttime.shareInstance (). GetCostTime ()); // Mulai rekaman waktu b2statisticcosttime.shareinstance (). Start (); sleep (100); // cetak waktu mulai dari B2system.out.println ("b2-starttime:"+statisticcosttime.shareInstance (). GetStartTime ()); // end record waktu b2statisticcosttime.shareinstance (). End (); // cetak waktu biaya cetak dari B2system.out.println ("b2:"+statisticcosttime.shareInstance (). GetCosttime ());} catch (pengecualian e) {}}; b.start ();}}Setelah menjalankan kode, hasil output adalah sebagai berikut: Keakuratan hasil output adalah nanodetik.
Itu tergantung pada apakah hasilnya berbeda dari yang kami harapkan. Saya menemukan bahwa hasil A harus kira -kira sama dengan B1+B2. Kenapa menjadi sama dengan B2? Jawabannya adalah bahwa ketika kita menentukan variabel starttime dan biaya, niat asli tidak boleh dibagikan, tetapi harus eksklusif untuk utas. Di sini variabel dibagikan dengan singleton, jadi ketika menghitung nilai a, startTime sebenarnya telah dimodifikasi oleh B2, jadi hasil yang sama dengan B2 adalah output.
Sekarang mari kita buka bagian yang dikomentari dalam StatisticCosttime dan cobalah dengan mengubahnya menjadi metode deklarasi Threadlocal.
Lihat hasilnya:
Ah! Ini mencapai efek yang diharapkan. Pada saat ini, beberapa siswa akan mengatakan bahwa ini bukan akses yang sesuai dengan utas, dan dapatkah saya memastikan keamanan utas selama saya menggunakan threadlocal? Jawabannya adalah tidak! Pertama -tama, kita bisa mencari tahu mengapa ada masalah keamanan utas, tetapi hanya ada dua situasi:
1. Anda telah berbagi sumber daya yang tidak boleh dibagikan di antara utas;
2. Anda tidak menjamin akses tertib ke sumber daya yang dibagikan antar utas;
Yang pertama dapat diselesaikan dengan metode "waktu pertukaran spasial", menggunakan threadlocal (Anda juga dapat secara langsung mendeklarasikan variabel lokal utas), dan yang terakhir dapat diselesaikan dengan metode "waktu pertukaran spasial", yang jelas bukan apa yang bisa dilakukan Threadlocal.
3. Prinsip Threadlocal
Prinsip implementasi sebenarnya sangat sederhana. Setiap kali operasi baca dan tulis ke objek threadlocal sebenarnya adalah operasi baca dan tulis ke objek nilai utas; Di sini kami mengklarifikasi bahwa tidak ada pembuatan salinan variabel, karena ruang memori yang dialokasikan oleh variabel tidak digunakan untuk menyimpan objek T, tetapi nilai -nilai utas digunakan untuk menyimpan objek T; Setiap kali kami memanggil metode set threadlocal di utas, itu sebenarnya adalah proses penulisan objek ke objek nilai yang sesuai utas; Saat memanggil metode get threadlocal, itu sebenarnya adalah proses mendapatkan objek dari objek nilai yang sesuai utas.
Lihat kode sumbernya:
Set variabel anggota threadlocal
/*** Menetapkan nilai variabel ini untuk utas saat ini. Jika diatur ke * {@code null}, nilainya akan diatur ke null dan entri yang mendasarinya akan * masih ada. * * @param nilai nilai baru dari variabel untuk utas penelepon. */public void set (nilai t) {thread currentThread = thread.currentThread (); Nilai nilai = nilai (currentThread); if (values == null) {values = initializevalues (currentThread); } values.put (this, value);}Metode anggota treadlocal
/*** Mengembalikan nilai variabel ini untuk utas saat ini. Jika entri * belum ada untuk variabel ini pada utas ini, metode ini akan * membuat entri, mengisi nilai dengan hasil * {@link #InitialValue ()}. * * @return Nilai variabel saat ini untuk utas panggilan. */@SuppressWarnings ("Uncecked") public t get () {// Dioptimalkan untuk jalur cepat. Thread currentThread = thread.currentThread (); Nilai nilai = nilai (currentThread); if (values! = null) {objek [] tabel = values.table; int index = hash & values.mask; if (this.reference == tabel [index]) {return (t) tabel [index + 1]; }} else {values = initializevalues (currentThread); } return (t) values.getAftermiss (this);}Metode Inisialisasi Metode Threadlocal
/*** Membuat contoh nilai untuk utas ini dan jenis variabel. */Value initializeValues (arus utas) {return current.localValues = new values ();}Nilai Metode Anggota Threadlocal
/*** Mendapat instance nilai untuk utas ini dan jenis variabel. */Nilai nilai (arus utas) {return current.localValues;}Jadi bagaimana Anda membaca dan menulis objek dalam nilai ini?
Nilai ada sebagai kelas internal threadlocal; Nilai -nilai ini termasuk objek array penting [], yang merupakan bagian penting dari menjawab pertanyaan. Ini digunakan untuk menyimpan berbagai jenis variabel treadlokal di utas. Jadi pertanyaannya adalah, bagaimana Anda memastikan bahwa Anda tidak mendapatkan nilai dari jenis lain saat mengambil variabel dari jenis tertentu? Secara umum, peta akan dipetakan sesuai dengan nilai kunci; Ya, idenya adalah ide ini, tetapi tidak diimplementasikan dengan peta di sini, itu adalah mekanisme peta yang diimplementasikan dengan objek []; Namun, jika Anda ingin menggunakan peta untuk memahaminya, itu tidak mungkin karena mekanismenya sama; Kunci sebenarnya sesuai dengan referensi lemah threadlocal, dan nilainya sesuai dengan objek yang kami lewati.
Mari kita jelaskan cara menggunakan objek [] untuk mengimplementasikan mekanisme peta (lihat Gambar 1); Ini menggunakan paritas subskrip array untuk membedakan kunci dan nilai, yaitu tabel di bawah ini menyimpan kunci pada posisi genap dan angka ganjil menyimpan nilai. Beginilah cara dilakukan. Jika siswa yang tertarik ingin mengetahui implementasi algoritma, mereka dapat mempelajarinya secara mendalam, saya tidak akan menjelaskannya secara rinci di sini.
Berdasarkan contoh pertama sebelumnya, situasi penyimpanan dianalisis:
Ketika program dieksekusi, ada tiga utas A, B dan utama. Ketika name.set () dipanggil di utas, tiga ruang memori yang identik dialokasikan di area heap untuk tiga instance utas pada saat yang sama untuk menyimpan objek nilai, menggunakan referensi nama sebagai kunci, dan objek spesifik disimpan sebagai nilai dalam tiga objek yang berbeda [] (lihat gambar di bawah):
4. Ringkasan
Threadlocal tidak dapat sepenuhnya menyelesaikan masalah konkurensi selama pemrograman multi-thread. Masalah ini juga membutuhkan solusi yang berbeda untuk dipilih sesuai dengan situasi yang berbeda, "Space for Time" atau "Time for Space".
Fungsi terbesar dari threadlocal adalah mengubah variabel yang dibagikan utas menjadi variabel thread-lokal untuk mencapai isolasi antar utas.
Di atas adalah tentang pemahaman Threadlocal di Java dengan cepat. Saya harap ini akan membantu semua orang. Jika ada kekurangan, silakan tinggalkan pesan untuk menunjukkannya. Terima kasih teman atas dukungan Anda untuk situs ini.