Apa itu java multithreading
Java menyediakan mekanisme untuk menangani banyak tugas secara bersamaan (secara bersamaan dan mandiri). Beberapa utas hidup berdampingan dalam proses JVM yang sama, sehingga mereka berbagi ruang memori yang sama. Dibandingkan dengan banyak proses, komunikasi antara beberapa utas lebih ringan. Dalam pemahaman saya, Java Multithreading sepenuhnya meningkatkan pemanfaatan CPU. Java Thread memiliki 4 negara bagian: baru, dapat dijalankan, diblokir, dan mati. Kuncinya adalah memblokir. Memblokir berarti menunggu. Utas pemblokiran tidak berpartisipasi dalam alokasi slice waktu operator utas, jadi tentu saja itu tidak akan menggunakan CPU. Dalam lingkungan multithreaded, utas yang tidak diblokir berjalan dan memanfaatkan CPU secara penuh.
Ringkasan 40 pertanyaan
1. Apa gunanya multi-threading?
Sebuah pertanyaan yang mungkin tampak omong kosong bagi banyak orang: Saya bisa menggunakan multi-threading, tapi apa gunanya? Menurut pendapat saya, jawaban ini bahkan lebih tidak masuk akal. Yang disebut "mengetahui apa yang benar" adalah "mengetahui apa yang benar", "mengetahui apa yang benar", "mengapa penggunaan" adalah "mengetahui apa yang benar". Hanya dengan mencapai tingkat "mengetahui apa yang benar" dapat dikatakan dapat menerapkan titik pengetahuan secara bebas. Oke, izinkan saya memberi tahu Anda apa yang saya pikirkan tentang masalah ini:
(1) Berikan permainan penuh untuk keuntungan CPU multi-core
Dengan kemajuan industri, laptop saat ini, desktop dan bahkan server aplikasi komersial semuanya dual-core, dan 4-core, 8-core atau bahkan 16-core tidak jarang. Jika itu adalah program utamanya, 50% terbuang pada CPU dual-core dan 75% terbuang pada CPU 4-core. Yang disebut "multi-threading" pada CPU inti tunggal adalah multi-threading palsu. Prosesor hanya akan memproses sepotong logika pada saat yang sama, tetapi utas beralih relatif cepat, yang terlihat seperti banyak utas berjalan "pada saat yang sama". Multithreading pada CPU multi-core adalah multithreading yang sebenarnya. Ini dapat memungkinkan logika multi-segmen Anda untuk bekerja pada saat yang sama dan multi-threading. Ini benar-benar dapat memberikan permainan penuh pada keunggulan CPU multi-core untuk mencapai tujuan memanfaatkan CPU secara penuh.
(2) mencegah penyumbatan
Dari perspektif efisiensi operasi program, CPU inti tunggal tidak hanya tidak hanya akan memberikan permainan penuh untuk keuntungan multi-threading, tetapi sebaliknya akan mengurangi efisiensi keseluruhan program karena menjalankan multi-threading pada CPU inti tunggal akan menyebabkan switching konteks utas, yang akan mengurangi efisiensi keseluruhan program. Namun, kita masih perlu menerapkan multithreading ke CPU inti tunggal untuk mencegah penyumbatan. Bayangkan saja, jika CPU inti tunggal menggunakan satu utas, selama utas diblokir, misalnya, membaca dari jarak jauh data tertentu, rekannya belum kembali untuk waktu yang lama dan belum menetapkan waktu tunggu, maka seluruh program Anda akan berhenti berjalan sebelum data dikembalikan. Multi-threading dapat mencegah masalah ini. Beberapa utas dijalankan pada saat yang sama. Bahkan jika kode satu utas diblokir dari data membaca, itu tidak akan mempengaruhi eksekusi tugas lain.
(3) mudah dimodelkan
Ini adalah keuntungan lain yang tidak begitu jelas. Misalkan ada tugas besar A, pemrograman tunggal, maka Anda perlu banyak mempertimbangkan, dan lebih merepotkan untuk membangun seluruh model program. Namun, jika Anda memecahkan tugas besar A ini menjadi beberapa tugas kecil, Tugas B, Tugas C, dan Tugas D, membangun model program secara terpisah, dan menjalankan tugas -tugas ini secara terpisah melalui banyak utas, itu akan jauh lebih sederhana.
2. Cara membuat utas
Masalah yang lebih umum umumnya dua:
(1) mewarisi kelas utas
(2) Menerapkan antarmuka runnable
Sedangkan yang lebih baik, tidak perlu dikatakan bahwa yang terakhir jelas lebih baik, karena cara untuk mengimplementasikan antarmuka lebih fleksibel daripada metode kelas warisan, dan juga dapat mengurangi kopling antar program. Pemrograman berorientasi antarmuka juga merupakan inti dari enam prinsip utama pola desain.
3. Metode Perbedaan Antara Mulai ()
Hanya ketika metode start () dipanggil akan karakteristik multi-threading ditampilkan, dan kode dalam metode run () dari utas yang berbeda akan dieksekusi secara bergantian. Jika Anda hanya memanggil metode run (), kode akan dieksekusi secara serempak. Anda harus menunggu kode dalam metode run () dari satu utas untuk dieksekusi sebelum utas lain dapat menjalankan kode dalam metode run ().
4. Perbedaan antara antarmuka runnable dan antarmuka yang dapat dipanggil
Ada sedikit pertanyaan yang mendalam, dan itu juga menunjukkan luasnya pengetahuan yang dipelajari oleh seorang programmer Java.
Nilai pengembalian metode run () di antarmuka runnable batal, dan apa yang dilakukannya adalah hanya untuk menjalankan kode dalam metode run (); Metode Call () dalam antarmuka yang dapat dipanggil memiliki nilai pengembalian, yang merupakan generik, dan dapat digunakan untuk mendapatkan hasil eksekusi asinkron dalam hubungannya dengan Future dan Futuretask.
Ini sebenarnya adalah fitur yang sangat berguna, karena alasan utama mengapa multithreading lebih sulit dan lebih kompleks daripada threading tunggal adalah bahwa multithreading penuh dengan yang tidak diketahui. Apakah utas tertentu telah dieksekusi? Berapa lama utas telah dieksekusi? Apakah data yang kami harapkan saat utas dieksekusi? Tidak menyadari bahwa yang bisa kita lakukan hanyalah menunggu tugas multi-threaded ini dieksekusi. Callable+Future/FutureTask dapat memperoleh hasil berjalan multi-threaded, dan dapat membatalkan tugas utas ketika waktu tunggu terlalu lama dan data yang diperlukan tidak diperoleh. Ini sangat berguna.
5. Perbedaan antara Cyclicbarrier dan Countdownlatch
Kedua kelas yang terlihat agak serupa dapat digunakan untuk menunjukkan bahwa kode berjalan pada titik tertentu di bawah java.util.concurrent. Perbedaan antara keduanya adalah:
(1) Setelah utas cyclicbarrier berjalan pada titik tertentu, utas berhenti berjalan. Sampai semua utas mencapai titik ini, semua utas tidak akan berjalan lagi; Countdownlatch tidak. Setelah utas berjalan pada titik tertentu, itu hanya memberikan nilai -1 tertentu, dan utas terus berjalan.
(2) Cyclicbarrier hanya dapat membangkitkan satu tugas, Countdownlatch dapat membangkitkan banyak tugas
(3) Cyclicbarrier dapat digunakan kembali, Countdownlatch tidak dapat digunakan kembali, dan nilai jumlahnya adalah 0 tidak akan digunakan kembali, dan Countdownlatch tidak akan digunakan lagi
6. Peran kata kunci yang mudah menguap
Masalah yang sangat penting adalah bahwa setiap programmer Java yang belajar dan menerapkan multi-threading harus menguasainya. Prasyarat untuk memahami peran kata kunci yang mudah menguap adalah memahami model memori Java. Saya tidak akan berbicara tentang model memori Java di sini. Anda dapat merujuk pada titik 31. Ada dua fungsi utama dari kata kunci yang mudah menguap:
(1) Multithreading terutama berputar di sekitar dua karakteristik visibilitas dan atomisitas. Variabel yang dimodifikasi oleh kata kunci yang mudah menguap memastikan visibilitasnya antara beberapa utas, yaitu, setiap kali variabel volatil dibaca, itu harus menjadi data terbaru.
(2) Eksekusi kode yang mendasarinya tidak sesederhana bahasa tingkat tinggi yang kita lihat - program Java. Eksekusinya adalah kode Java -> bytecode -> Jalankan kode C/C ++ yang sesuai sesuai dengan kode bytecode -> C/C ++ dikompilasi ke dalam bahasa perakitan -> berinteraksi dengan sirkuit perangkat keras. Pada kenyataannya, untuk mendapatkan kinerja yang lebih baik, JVM dapat memesan ulang instruksi, dan beberapa masalah yang tidak terduga mungkin muncul di bawah multi-threading. Menggunakan volatile akan melarang pemesanan ulang semantik, yang tentu saja mengurangi efisiensi eksekusi kode sampai batas tertentu
Dari sudut pandang praktis, peran penting dari volatil adalah bergabung dengan CAS untuk memastikan atomisitas. Untuk detailnya, Anda dapat merujuk ke kelas di bawah paket java.util.concurrent.atomic, seperti AtomicInteger.
7. Apa itu keamanan utas
Pertanyaan teoretis lainnya, ada banyak jawaban. Saya ingin memberikan satu yang saya pikir secara pribadi adalah penjelasan terbaik: jika kode Anda selalu bisa mendapatkan hasil yang sama ketika dieksekusi di bawah multithreading dan satu threading, maka kode Anda aman.
Ada sesuatu yang layak disebutkan tentang masalah ini, yaitu, ada beberapa tingkat keamanan utas:
(1) Immutable
Seperti string, integer, panjang, dll., Mereka semua adalah tipe terakhir. Tidak ada utas yang dapat mengubah nilainya. Jika Anda ingin mengubahnya, Anda tidak akan membuat yang baru. Oleh karena itu, objek yang tidak dapat diubah ini dapat digunakan secara langsung di lingkungan multi-utuh tanpa sarana sinkronisasi apa pun.
(2) Keamanan utas absolut
Terlepas dari lingkungan runtime, penelepon tidak memerlukan langkah -langkah sinkronisasi tambahan. Untuk melakukan ini, Anda biasanya harus membayar banyak biaya tambahan. Di Java, sebenarnya bukan kelas yang aman. Namun, ada juga kelas yang benar-benar aman, seperti copyonWriteArrayList dan copyonWriteArrayset
(3) Keamanan utas relatif
Keselamatan utas relatif adalah apa yang biasanya kita sebut keamanan utas. Misalnya, vektor, tambah dan menghapus metode adalah operasi atom dan tidak akan terganggu, tetapi terbatas pada ini. Jika utas yang melintasi vektor tertentu, utas ditambahkan pada saat yang sama, ConcurrentModification Exception akan terjadi pada 99% kasus, yang merupakan mekanisme gagal-cepat.
(4) Benang tidak aman
Tidak ada yang bisa dikatakan tentang ini. ArrayList, LinkedList, HashMap, dll. Semuanya adalah kelas non-utas.
8. Cara Mendapatkan File Dump Thread di Java
Untuk masalah seperti dead loop, kebuntuan, pemblokiran, pembukaan halaman lambat, dll., Memukul dump adalah cara terbaik untuk menyelesaikan masalah. Dump utas yang disebut adalah tumpukan utas. Ada dua langkah untuk mendapatkan tumpukan utas:
(1) Dapatkan PID utas, Anda dapat menggunakan perintah JPS, dan Anda juga dapat menggunakan PS -EF | grep java di lingkungan linux
(2) Cetak tumpukan utas, Anda dapat menggunakan perintah JStack PID, dan Anda juga dapat menggunakan Kill -3 PID di lingkungan Linux
Juga, kelas utas menyediakan metode getStackTrace () yang juga dapat digunakan untuk mendapatkan tumpukan utas. Ini adalah metode instan, jadi metode ini terikat pada instance utas tertentu. Setiap kali Anda mendapatkan tumpukan saat ini berjalan di utas tertentu.
9. Apa yang terjadi jika utas memiliki pengecualian runtime?
Jika pengecualian ini tidak tertangkap, utas akan berhenti mengeksekusi. Poin penting lainnya adalah: jika utas ini memegang monitor objek tertentu, monitor objek akan segera dilepaskan
10. Cara berbagi data antara dua utas
Bagikan saja objek antar utas, dan kemudian membangkitkan dan menunggu melalui menunggu/memberi tahu/memberi tahu All, menunggu/sinyal/sinyal. Misalnya, memblokir blockingqueue antrean dirancang untuk berbagi data antar utas.
11. Apa perbedaan antara metode tidur dan metode tunggu
Pertanyaan ini sering ditanyakan, baik metode tidur dan metode tunggu dapat digunakan untuk menyerahkan CPU untuk jangka waktu tertentu. Perbedaannya adalah bahwa jika utas memegang monitor suatu objek, metode tidur tidak akan menyerahkan monitor objek ini, dan metode tunggu akan menyerahkan monitor objek ini.
12. Apa peran model konsumen produsen?
Pertanyaan ini teoretis, tetapi penting:
(1) Meningkatkan efisiensi operasi seluruh sistem dengan menyeimbangkan kapasitas produksi kapasitas konsumsi produsen dan konsumen, yang merupakan peran terpenting dari model konsumen produsen.
(2) Decoupling, ini adalah fungsi yang disertai dengan model produsen dan konsumen. Decoupling berarti ada lebih sedikit koneksi antara produsen dan konsumen. Semakin sedikit koneksi, semakin banyak mereka dapat berkembang sendiri tanpa menerima kendala timbal balik.
13. Apa penggunaan threadlocal
Sederhananya, Threadlocal adalah praktik pertukaran ruang untuk waktu. Setiap utas memelihara threadlocal.threadlocalmap yang diimplementasikan oleh metode alamat terbuka untuk mengisolasi data dan tidak berbagi data, sehingga secara alami tidak akan ada masalah keamanan utas.
14. Mengapa Metode Wait () dan Notify ()/NotifyAll () dipanggil di blok sinkronisasi
Ini dipaksa oleh JDK. Metode tunggu () dan beri tahu ()/notifyall () metode harus terlebih dahulu mendapatkan kunci objek sebelum menelepon
15. Apa perbedaan antara metode tunggu () dan notify ()/notifyall () metode saat meninggalkan monitor objek
Perbedaan antara metode tunggu () dan metode notify ()/notifyAll () ketika menyerahkan monitor objek adalah bahwa metode tunggu () segera melepaskan monitor objek, sedangkan metode notify ()/notifyall () akan menunggu kode yang tersisa dari utas untuk dieksekusi sebelum melepaskan monitor objek.
16. Mengapa menggunakan kumpulan benang
Hindari pembuatan dan penghancuran utas yang sering untuk mencapai penggunaan kembali objek benang. Selain itu, menggunakan kumpulan utas juga dapat secara fleksibel mengontrol jumlah konkurensi sesuai dengan proyek.
17. Cara mendeteksi apakah utas memegang monitor objek
Saya juga melihat pertanyaan wawancara multithreaded di internet untuk mengetahui bahwa ada cara untuk menentukan apakah utas memegang monitor objek: kelas utas menyediakan metode holdslock (objek obj), yang akan mengembalikan true jika dan hanya jika monitor objek OBJ dipegang oleh utas. Perhatikan bahwa ini adalah metode statis, yang berarti bahwa "utas tertentu" mengacu pada utas saat ini.
18. Perbedaan antara sinkronisasi dan reentrantlock
Sinkronisasi adalah kata kunci yang sama seolah -olah, untuk, untuk, dan sementara, dan reentrantlock adalah kelas, yang merupakan perbedaan penting antara keduanya. Karena Reentrantlock adalah kelas, ia menyediakan fitur yang lebih dan lebih fleksibel daripada disinkronkan, yang dapat diwariskan, memiliki metode, dan memiliki berbagai variabel kelas. Skalabilitas reentrantlock dari yang disinkronkan tercermin dalam beberapa titik:
(1) Reentrantlock dapat mengatur waktu tunggu untuk mendapatkan kunci, sehingga menghindari kebuntuan
(2) Reentrantlock dapat memperoleh berbagai informasi kunci
(3) Reentrantlock dapat secara fleksibel menerapkan pemberitahuan multi-channel secara fleksibel
Selain itu, mekanisme penguncian keduanya sebenarnya berbeda. Reentrantlock yang mendasarinya memanggil metode taman yang tidak aman untuk mengunci, dan operasi yang disinkronkan harus menjadi kata tanda di header objek, saya tidak dapat memastikan hal ini.
19. Apa konkurensi concurrenthashmap
Konkurensi concurrenthashmap adalah ukuran segmen, yang merupakan 16 secara default, yang berarti bahwa paling banyak 16 utas dapat beroperasi concurrenthashmap pada saat yang sama. Ini juga merupakan keuntungan terbesar dari ConcurrenthashMap di Hashtable. Bagaimanapun, dapatkah hashtable memiliki dua utas pada saat yang sama mendapatkan data di hashtable?
20. Apa itu ReadWritelock
Pertama -tama, mari kita jelaskan bahwa bukan karena reentrantlock tidak baik, hanya saja reentrantlock terbatas pada beberapa waktu. Jika reentrantlock digunakan, itu mungkin untuk mencegah ketidakkonsistenan data yang disebabkan oleh data penulisan data dan data bacaan utas B. Namun, dengan cara ini, jika Thread C membaca data dan utas D juga membaca data, membaca data tidak akan mengubah data. Tidak perlu menguncinya, tetapi masih menguncinya, yang mengurangi kinerja program.
Karena itu, Readwritelock kunci baca-tulis lahir. ReadWritelock adalah antarmuka kunci baca-tulis. ReentrantReadWritelock adalah implementasi konkret dari antarmuka ReadWritelock, yang mewujudkan pemisahan baca dan tulis. Kunci baca dibagikan dan kunci tulis eksklusif. Baca dan baca dan baca tidak akan saling eksklusif. Hanya membaca dan menulis, menulis, membaca, menulis, dan menulis akan saling eksklusif, meningkatkan kinerja baca dan tulis.
21. Apa itu Futuretask
Ini sebenarnya disebutkan sebelumnya. Futuretask mewakili tugas operasi asinkron. Kelas implementasi tertentu dari Callable dapat diteruskan ke Futuretask, yang dapat menunggu hasil operasi asinkron ini untuk mendapatkan, menentukan apakah itu telah selesai, dan membatalkan tugas. Tentu saja, karena Futuretask juga merupakan kelas implementasi dari antarmuka yang dapat dijalankan, Futuretask juga dapat ditempatkan di kumpulan utas.
22. Cara menemukan utas mana yang menggunakan CPU terpanjang di lingkungan Linux
Ini adalah masalah yang lebih praktis, dan saya pikir masalah ini cukup bermakna. Anda dapat melakukan ini:
(1) Dapatkan PID proyek, JPS atau PS -EF | grep java, yang telah disebutkan sebelumnya
(2) Top -H -P PID, pesanan tidak dapat diubah
Ini akan mencetak persentase waktu CPU yang dibutuhkan proyek saat ini untuk setiap utas. Perhatikan bahwa yang di sini adalah LWP, yang merupakan nomor utas dari utas asli sistem operasi. Gunung notebook saya tidak menggunakan proyek java di lingkungan Linux, jadi tidak ada cara untuk mengambil tangkapan layar dan demonstrasi. Jika perusahaan menggunakan proyek menggunakan lingkungan Linux, Anda dapat mencobanya.
Menggunakan "Top -H -P PID" + "JPS PID" dapat dengan mudah menemukan tumpukan utas yang menempati CPU tinggi, sehingga memposisikan alasan hunian CPU tinggi, yang umumnya disebabkan oleh operasi kode yang tidak tepat yang mengarah pada loop dead.
Akhirnya, izinkan saya menyebutkan bahwa LWP bermain dengan "Top -H -P PID" adalah desimal, dan nomor utas lokal dimainkan dengan "JPS PID" adalah hexadecimal. Setelah konversi, Anda dapat menemukan tumpukan utas saat ini yang menempati CPU tinggi.
23. Pemrograman Java Menulis program yang akan menyebabkan kebuntuan
Saya melihat pertanyaan ini untuk pertama kalinya dan berpikir itu adalah pertanyaan yang sangat bagus. Banyak orang tahu seperti apa kebuntuan: Thread A dan Thread B sedang menunggu kunci masing -masing untuk menyebabkan loop mati yang tak terbatas untuk melanjutkan program. Tentu saja, ini hanya terbatas pada ini. Jika Anda bertanya cara menulis program kebuntuan, Anda tidak akan tahu. Terus terang, Anda tidak mengerti apa kebuntuan itu. Jika Anda memahami suatu teori, Anda akan selesai. Anda pada dasarnya tidak dapat melihat masalah kebuntuan dalam praktik.
Untuk benar -benar memahami apa kebuntuan, pertanyaan ini tidak sulit, ada beberapa langkah:
(1) Dua utas memegang dua objek objek: masing -masing LOCK1 dan LOCK2. Kedua kunci ini berfungsi sebagai kunci untuk blok kode sinkron;
(2) Dalam metode run () utas 1, blok kode sinkronisasi pertama -tama memperoleh kunci objek LOCK1, thread.sleep (xxx), waktunya tidak memakan waktu terlalu banyak, 50 milidetik hampir sama, dan kemudian mendapatkan kunci objek LOCK2. Ini terutama dilakukan untuk mencegah benang 1 dari terus mendapatkan kunci objek dari dua objek: LOCK1 dan LOCK2.
(3) Jalankan utas 2) (dalam metode ini, blok kode sinkronisasi pertama -tama memperoleh Objek LOCK2, dan kemudian memperoleh Objek Lock1. Tentu saja, Object Lock1 sudah dipegang oleh Lock 1 Thread 1, dan Thread 2 harus menunggu Thread 1 untuk melepaskan Objek LOCK1 LOCK1.
Dengan cara ini, setelah utas 1 "tidur" dan Thread 2 telah memperoleh Object Lock2. Thread 1 mencoba untuk mendapatkan Object Lock2 saat ini, dan diblokir. Pada saat ini, kebuntuan terbentuk. Saya tidak akan menulis kodenya lagi, ini membutuhkan banyak ruang. Java Multithreading 7: Deadlock Artikel ini berisi implementasi kode dari langkah -langkah di atas.
24. Cara membangunkan utas pemblokiran
Jika benang blok karena panggilan tunggu (), sleep () atau gabungan (), itu dapat mengganggu utas dan membangunkannya dengan melempar Exception interrupted; Jika utas mengalami penyumbatan IO, itu tidak berdaya karena IO diimplementasikan oleh sistem operasi, dan kode Java tidak dapat secara langsung menghubungi sistem operasi.
25. Bantuan Apa yang Dilakukan Objek Immutable Membantu Multithreading
Masalah yang disebutkan di atas adalah bahwa objek yang tidak dapat diubah memastikan visibilitas memori objek, dan tidak perlu sinkronisasi tambahan untuk membaca objek yang tidak dapat diubah, yang meningkatkan efisiensi eksekusi kode.
26. Apa itu switching konteks multi-threaded
Pergantian konteks multithreaded mengacu pada proses pengalihan kontrol CPU dari satu utas yang berjalan ke utas lain yang siap dan menunggu hak eksekusi CPU diperoleh.
27. Jika antrian kumpulan utas penuh saat Anda mengirimkan tugas, apa yang akan terjadi saat ini
Jika Anda menggunakan LinkedBlockingQueue, yaitu antrian yang tidak terikat, itu tidak masalah. Terus tambahkan tugas ke antrian pemblokiran dan tunggu eksekusi, karena LinkedBlockingQueue hampir dapat dianggap sebagai antrian tak terbatas dan dapat menyimpan tugas tanpa batas; Jika Anda menggunakan antrian terikat, misalnya, arrayblockingqueue, tugas akan ditambahkan ke arrayblockingqueue terlebih dahulu. Jika arrayblockingqueue penuh, RejectExecutionHandler akan menggunakan kebijakan penolakan untuk menangani tugas penuh, dan standarnya dibatalkan.
28. Apa algoritma penjadwalan utas yang digunakan di java?
Gaya preemptive. Setelah utas menggunakan CPU, sistem operasi akan menghitung prioritas total berdasarkan data seperti prioritas utas, kelaparan utas, dll. Dan mengalokasikan iris waktu berikutnya ke utas untuk dieksekusi.
29. Apa fungsi thread.sleep (0)
Pertanyaan ini terkait dengan pertanyaan di atas, dan saya bersama -sama. Karena Java menggunakan algoritma penjadwalan utas preemptive, mungkin terjadi bahwa utas sering mendapatkan kontrol CPU. Untuk memungkinkan beberapa utas dengan prioritas yang relatif rendah untuk mendapatkan kontrol CPU, thread.sleep (0) dapat digunakan untuk memicu sistem operasi yang secara manual mengalokasikan irisan waktu, yang juga merupakan operasi untuk menyeimbangkan kontrol CPU.
30. Apa itu putaran
Banyak kode yang disinkronkan hanyalah beberapa kode yang sangat sederhana, dan waktu eksekusi sangat cepat. Mengunci utas yang menunggu saat ini mungkin merupakan operasi yang tidak bermanfaat, karena pemblokiran utas melibatkan pergantian negara-pengguna dan keadaan-keadaan. Karena kode yang disinkronkan dijalankan dengan sangat cepat, Anda mungkin juga membiarkan utas menunggu kunci tidak diblokir, tetapi sebaliknya melakukan loop sibuk di batas disinkronkan. Ini putaran. Jika Anda telah melakukan beberapa loop sibuk dan menemukan bahwa kunci belum diperoleh, dan kemudian memblokirnya, ini mungkin strategi yang lebih baik.
31. Apa itu model memori java
Model memori Java mendefinisikan spesifikasi untuk akses multi-threading ke memori Java. Model memori Java perlu dijelaskan sepenuhnya, tetapi saya tidak bisa menjelaskannya dengan jelas dalam beberapa kalimat di sini. Izinkan saya merangkumnya secara singkat.
Beberapa bagian dari model memori Java:
(1) Model memori Java membagi memori menjadi memori utama dan memori kerja. Keadaan kelas, yaitu, variabel yang dibagikan antar kelas, disimpan dalam memori utama. Setiap kali utas Java menggunakan variabel -variabel ini di memori utama, ia akan membaca variabel dalam memori utama dan membiarkannya ada di memori kerjanya sendiri. Saat menjalankan kode utasnya sendiri, ia menggunakan variabel -variabel ini dan mengoperasikan yang dalam memori kerjanya sendiri. Setelah kode utas dijalankan, nilai terbaru akan diperbarui ke memori utama.
(2) Beberapa operasi atom didefinisikan untuk mengoperasikan variabel dalam memori utama dan memori kerja
(3) Tentukan aturan untuk menggunakan variabel volatile
(4) Terjadi sebelum, yaitu prinsip kejadian pertama, mendefinisikan beberapa aturan di mana operasi A harus terjadi pertama dalam operasi B. Misalnya, kode di depan aliran kontrol di utas yang sama harus terjadi terlebih dahulu dalam kode di balik aliran kontrol, aksi pelepasan lock yang dibuka harus terjadi terlebih dahulu dalam tindakan mengunci kunci yang sama, dll. Selama aturan ini dipenuhi. Jika sepotong kode tertentu tidak mematuhi semua aturan yang terjadi sebelum, maka sepotong kode ini harus tidak aman.
32. Apa itu CAS
CAS, nama lengkap Bandingkan dan setel, dibandingkan-set. Misalkan ada tiga operan: nilai memori V, nilai yang diharapkan lama A, nilai B yang akan dimodifikasi. Jika dan hanya jika nilai A dan nilai memori yang diharapkan adalah sama, nilai memori akan dimodifikasi ke B dan dikembalikan benar, jika tidak, tidak ada yang akan dilakukan dan salah akan dikembalikan. Tentu saja, CAS harus bekerja sama dengan variabel yang mudah menguap, sehingga dapat memastikan bahwa variabel yang diperoleh setiap kali adalah nilai terbaru dalam memori utama. Kalau tidak, nilai yang diharapkan lama A akan selalu menjadi nilai A yang tidak akan berubah untuk utas. Selama operasi CAS tertentu gagal, itu tidak akan pernah berhasil.
33. Apa kunci optimis dan kunci pesimistis
(1) Kunci Optimis: Sama seperti namanya, ini optimis tentang masalah keamanan utas yang disebabkan oleh operasi bersamaan. Kunci optimis percaya bahwa persaingan tidak selalu terjadi, sehingga tidak perlu menahan kunci, dan akan membandingkan - mengatur kedua tindakan ini sebagai operasi atom untuk mencoba memodifikasi variabel dalam memori. Jika gagal, itu berarti konflik terjadi, dan kemudian harus ada logika coba lagi yang sesuai.
(2) Kunci pesimistis: Sama seperti namanya, ini pesimistis tentang masalah keselamatan utas yang disebabkan oleh operasi bersamaan. Kunci pesimistis percaya bahwa persaingan akan selalu terjadi. Oleh karena itu, setiap kali sumber daya dioperasikan, ia akan memiliki kunci eksklusif, seperti halnya disinkronkan, terlepas dari apakah dikunci secara langsung, dan sumber daya akan dioperasikan.
34. Apa itu aqs
Mari kita bicara secara singkat tentang AQS. Nama lengkap AQS adalah AbstractQueuedsyshronizer. Ini harus menjadi sinkronisasi antrian abstrak saat diterjemahkan.
Jika dasar java.util.concurrent adalah CAS, maka AQS adalah inti dari seluruh paket konkurensi Java, dan Reentrantlock, Countdownlatch, Semaphore, dll. Semua menggunakannya. AQS sebenarnya menghubungkan semua entri dalam bentuk antrian dua arah. Misalnya, Reentrantlock. Semua utas menunggu ditempatkan di entri dan terhubung ke antrian dua arah. Jika utas sebelumnya menggunakan Reentrantlock, maka entri pertama dari antrian dua arah sebenarnya mulai berjalan.
AQS mendefinisikan semua operasi pada antrian dua arah, tetapi hanya membuka metode trylock dan tryrelease untuk digunakan pengembang. Pengembang dapat menulis ulang metode trylock dan tryrelease sesuai dengan implementasi mereka sendiri untuk mengimplementasikan fungsi konkurensi mereka sendiri.
35. Keamanan utas mode singleton
Masalah klise, hal pertama yang perlu dikatakan adalah bahwa keselamatan utas dari pola singleton berarti bahwa: contoh kelas tertentu hanya akan dibuat sekali dalam lingkungan multi-threaded. Ada banyak cara untuk menulis pola singleton, izinkan saya meringkas:
(1) Menulis pola singleton pria lapar: keamanan utas
(2) Menulis Pola Singleton Malas: Non-Thread-Safe
(3) Tulis Mode Singleton Kunci Periksa Double: Keamanan Thread
36. Apa fungsi semaphore
Semaphore adalah semaphore, dan fungsinya adalah membatasi jumlah konkurensi dalam blok kode tertentu. Semaphore memiliki konstruktor yang dapat lulus integer int, menunjukkan bahwa hanya n utas yang dapat mengakses sepotong kode tertentu. Jika N terlampaui, harap tunggu sampai utas melengkapi blok kode dan masukkan utas berikutnya. Dari sini kita dapat melihat bahwa jika int integer n = 1 lulus dalam konstruktor semaphore setara dengan menjadi sinkronisasi.
37. Hanya ada satu pernyataan "jumlah pengembalian" dalam metode hashtable size (), jadi mengapa Anda masih perlu menyinkronkan?
Ini adalah kebingungan yang saya miliki sebelumnya, dan saya bertanya -tanya apakah Anda telah memikirkan pertanyaan ini. Jika ada beberapa pernyataan dalam suatu metode dan mereka semua mengoperasikan variabel kelas yang sama, maka jika Anda tidak menambahkan kunci di lingkungan multi-threaded, itu pasti akan menyebabkan masalah keamanan utas. Ini mudah dimengerti, tetapi metode size () jelas hanya memiliki satu pernyataan, jadi mengapa Anda masih perlu menambahkan kunci?
Mengenai masalah ini, saya telah memahaminya melalui bekerja dan belajar perlahan, dan ada dua alasan utama:
(1) Hanya satu utas yang dapat menjalankan metode sinkronisasi kelas tetap secara bersamaan, tetapi untuk metode asinkron kelas, beberapa utas dapat mengaksesnya secara bersamaan. Jadi, ada masalah. Mungkin Thread A menambahkan data saat mengeksekusi metode put hashtable, dan Thread B dapat memanggil metode size () secara normal untuk membaca jumlah elemen saat ini dalam hashtable. Nilai yang dibaca mungkin bukan yang terbaru. Mungkin Thread A telah menambahkan data, tetapi tanpa ukuran ++, Thread B telah membaca ukurannya. Jadi untuk utas B, ukuran yang dibaca harus tidak akurat. Setelah menambahkan sinkronisasi ke metode size (), itu berarti bahwa Thread B memanggil metode size () hanya setelah utas A memanggil metode put, yang memastikan keamanan utas
(2) CPU menjalankan kode, tetapi bukan kode Java. Ini sangat penting dan Anda harus mengingatnya. Kode Java akhirnya diterjemahkan ke dalam kode perakitan untuk dieksekusi, dan kode perakitan adalah kode yang benar -benar dapat berinteraksi dengan sirkuit perangkat keras. Bahkan jika Anda melihat bahwa hanya ada satu baris kode Java, dan bahkan jika Anda melihat bahwa kode Java dikompilasi, hanya ada satu baris bytecode yang dihasilkan, itu tidak berarti bahwa untuk lapisan yang mendasarinya, hanya ada satu operasi untuk pernyataan ini. Kalimat "jumlah pengembalian" dengan asumsi bahwa itu diterjemahkan ke dalam tiga pernyataan perakitan untuk dieksekusi, dan sangat mungkin bahwa utas akan beralih setelah kalimat pertama dieksekusi.
38. Benang mana yang merupakan konstruktor kelas utas dan blok statis yang dipanggil oleh utas mana
Ini adalah pertanyaan yang sangat rumit dan licik. Harap diingat: Konstruktor dan blok statis dari kelas utas dipanggil oleh utas di mana kelas utas baru berada, dan kode dalam metode run dipanggil oleh utas itu sendiri.
Jika pernyataan di atas membingungkan Anda, maka izinkan saya memberi Anda contoh, misalkan Thread1 baru ada di thread2 dan thread baru dalam fungsi utama, maka:
(1) Konstruktor Thread2 dan blok statis dipanggil oleh utas utama, dan metode run thread2 () dipanggil oleh thread2 itu sendiri
(2) Konstruktor dan blok statis Thread1 dipanggil oleh thread2, dan metode run () thread1 dipanggil oleh thread1 itu sendiri
39. Manakah pilihan yang lebih baik antara metode sinkronisasi dan blok sinkronisasi?
Sinkronisasi blok, yang berarti bahwa kode di luar blok sinkronisasi dieksekusi secara tidak sinkron, yang meningkatkan efisiensi kode lebih efisien daripada menyinkronkan seluruh metode. Harap Ketahui Satu Prinsip: Semakin sedikit rentang sinkronisasi
Semakin baik.
Dengan artikel ini, saya ingin menyebutkan bahwa meskipun semakin kecil kisaran sinkronisasi, semakin baik, masih ada metode optimasi yang disebut kasar dalam mesin virtual Java, yaitu untuk meningkatkan rentang sinkronisasi. Ini berguna. Misalnya, StringBuffer adalah kelas yang aman. Secara alami, metode append () yang paling umum digunakan adalah metode sinkronisasi. Ketika kami menulis kode, kami akan berulang kali menambahkan string, yang berarti mengunci berulang kali -> membuka kunci, yang tidak baik untuk kinerja, karena itu berarti bahwa mesin virtual Java harus berulang kali beralih antara keadaan kernel dan status pengguna pada utas ini. Oleh karena itu, mesin virtual Java melakukan operasi coarse kunci pada kode yang dipanggil dengan beberapa metode append, memperluas beberapa operasi append ke kepala dan ekor metode append, dan mengubahnya menjadi blok sinkronisasi besar. Ini mengurangi jumlah kunci-> waktu yang tidak terkunci, secara efektif meningkatkan efisiensi eksekusi kode.
40. Cara menggunakan kumpulan utas untuk bisnis dengan konkurensi tinggi dan waktu eksekusi tugas singkat? Bagaimana cara menggunakan kumpulan utas untuk bisnis dengan konkurensi rendah dan waktu eksekusi tugas yang lama? Bagaimana cara menggunakan kumpulan utas untuk bisnis dengan konkurensi tinggi dan waktu eksekusi layanan yang panjang?
Ini adalah pertanyaan yang saya lihat di situs web pemrograman bersamaan. Saya mengajukan pertanyaan ini terakhir dan berharap semua orang dapat melihat dan memikirkannya, karena pertanyaan ini sangat bagus, sangat praktis dan sangat profesional. Mengenai masalah ini, pendapat pribadi saya adalah:
(1) Konkurensi tinggi dan waktu pelaksanaan tugas singkat, jumlah utas utas yang dapat diatur ke nomor inti CPU +1 untuk mengurangi switching konteks utas
(2)并发不高、任务执行时间长的业务要区分开看:
a)假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以加大线程池中的线程数目,让CPU处理更多的业务
b)假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,和(1)一样吧,线程池中的线程数设置得少一些,减少线程上下文的切换
(3)并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步,至于线程池的设置,设置参考(2)。最后,业务执行时间长的问题,也可能需要分析一下,看看能不能使用中间件对任务进行拆分和解耦。
Java线程阻塞(Blocked)的类型:
调用sleep函数进入睡眠状态,Thread.sleep(1000)或者TimeUnit.SECONDS.sleep(1),sleep不会释放锁。
等待(wait)某个事件,分为两种,(wait,notify,notifyAll),(await, signal,signalAll) ,后面会详细介绍。wait和await会释放锁,且必须在获取到锁的环境才能调用。
等待锁,synchronized和lock环境中,锁已经被别的线程拿走,等待获取锁。
IO阻塞(Blocked),比如网络等待,文件打开,控制台读取。System.in.read()。