-
Karena beberapa thread dari proses yang sama berbagi ruang penyimpanan yang sama, selain memberikan kenyamanan, hal ini juga menimbulkan masalah konflik akses yang serius. Bahasa Java menyediakan mekanisme khusus untuk menyelesaikan konflik ini, yang secara efektif mencegah objek data yang sama diakses oleh beberapa thread secara bersamaan.
Karena kita dapat menggunakan kata kunci private untuk memastikan bahwa objek data hanya dapat diakses dengan metode, kita hanya perlu mengusulkan mekanisme untuk metode. Mekanisme ini adalah kata kunci tersinkronisasi, yang mencakup dua penggunaan: metode tersinkronisasi dan blok tersinkronisasi.
1. metode tersinkronisasi: mendeklarasikan metode tersinkronisasi dengan menambahkan kata kunci tersinkronisasi dalam deklarasi metode. menyukai:
void accessVal yang disinkronkan secara publik (int newVal);
Metode yang disinkronkan mengontrol akses ke variabel anggota kelas: setiap instance kelas berhubungan dengan kunci. Setiap metode yang disinkronkan harus mendapatkan kunci dari instance kelas yang memanggil metode tersebut sebelum dapat dieksekusi. Setelah metode ini dijalankan, metode tersebut secara eksklusif akan menempati variabel anggota kelas. Kunci tidak akan dilepaskan hingga kembali dari metode ini, setelah itu, thread yang diblokir dapat memperoleh kunci tersebut dan masuk kembali ke status yang dapat dieksekusi. Mekanisme ini memastikan bahwa untuk setiap instance kelas pada saat yang sama, paling banyak salah satu dari semua fungsi anggotanya yang dinyatakan sebagai tersinkronisasi berada dalam keadaan yang dapat dieksekusi (karena paling banyak seseorang dapat memperoleh kunci yang sesuai dengan instance kelas tersebut) (perhatikan pernyataan ini ! Karena Ini adalah kunci untuk instance kelas, jadi untuk suatu objek pada satu waktu, hanya satu metode tersinkronisasi yang dijalankan oleh satu thread pada satu waktu), sehingga secara efektif menghindari konflik akses variabel anggota kelas (selama semua metode itu mungkin) variabel anggota kelas akses dideklarasikan sebagai tersinkronisasi).
Di Java, tidak hanya instance kelas, setiap kelas juga berhubungan dengan kunci. Dengan cara ini, kita juga dapat mendeklarasikan fungsi anggota statis kelas sebagai tersinkronisasi untuk mengontrol aksesnya ke variabel anggota statis kelas.
Cacat metode tersinkronisasi: Jika metode besar dinyatakan tersinkronisasi, efisiensinya akan sangat terpengaruh. Biasanya, jika metode kelas thread run() dideklarasikan sebagai tersinkronisasi, maka metode tersebut akan terus berjalan sepanjang siklus hidup thread . Akan menyebabkan panggilannya ke metode tersinkronisasi apa pun di kelas ini tidak pernah berhasil. Tentu saja, kita dapat mengatasi masalah ini dengan memasukkan kode yang mengakses variabel anggota kelas ke dalam metode khusus, mendeklarasikannya sebagai tersinkronisasi, dan memanggilnya dalam metode utama, tetapi Java memberi kita solusi yang lebih baik, yaitu blok tersinkronisasi.
2. blok tersinkronisasi: mendeklarasikan blok tersinkronisasi melalui kata kunci tersinkronisasi. Sintaksnya adalah sebagai berikut:
disinkronkan(syncObject) {
//Kode untuk mengizinkan kontrol akses
}
Blok tersinkronisasi adalah blok kode yang kodenya harus mendapatkan kunci objek syncObject (seperti yang disebutkan sebelumnya, dapat berupa instance kelas atau kelas) sebelum dapat dieksekusi. Mekanisme spesifiknya sama seperti yang disebutkan di atas. Karena dapat menargetkan blok kode apa pun dan menentukan objek yang dikunci secara sewenang-wenang, ia memiliki fleksibilitas yang tinggi.
Pemblokiran thread (sinkronisasi)
Untuk mengatasi konflik akses ke area penyimpanan bersama, Java memperkenalkan mekanisme sinkronisasi. Sekarang mari kita periksa akses sumber daya bersama oleh banyak thread. Jelas, mekanisme sinkronisasi tidak lagi cukup, karena sumber daya yang diperlukan setiap saat mungkin tidak tersedia siap. Agar dapat diakses, pada gilirannya, mungkin ada lebih dari satu sumber daya yang siap pada saat yang bersamaan. Untuk mengatasi masalah kontrol akses dalam kasus ini, Java memperkenalkan dukungan untuk mekanisme pemblokiran.
Pemblokiran mengacu pada menghentikan sementara eksekusi thread untuk menunggu kondisi tertentu terjadi (misalnya sumber daya sudah siap). Siswa yang telah mempelajari sistem operasi harus memahaminya. Java menyediakan banyak metode untuk mendukung pemblokiran. Mari kita analisa satu per satu.
1. Metode sleep(): sleep() memungkinkan Anda menentukan periode waktu dalam milidetik sebagai parameter. Hal ini menyebabkan thread memasuki status pemblokiran dalam waktu yang ditentukan dan tidak dapat memperoleh waktu CPU thread kembali memasuki keadaan yang dapat dieksekusi.
Biasanya, sleep() digunakan saat menunggu sumber daya siap: setelah pengujian menemukan bahwa kondisi tidak terpenuhi, biarkan thread memblokir selama jangka waktu tertentu, lalu pengujian ulang hingga kondisi terpenuhi.
2. metode suspend() dan resume() (mudah menyebabkan kebuntuan, ketinggalan jaman): Kedua metode ini digunakan bersama-sama. suspend() menyebabkan thread memasuki status pemblokiran dan tidak akan pulih secara otomatis dipanggil. , dapatkah thread masuk kembali ke status yang dapat dieksekusi. Biasanya, suspend() dan resume() digunakan saat menunggu hasil yang dihasilkan oleh thread lain: setelah pengujian menemukan bahwa hasilnya belum dihasilkan, thread diblokir, dan setelah thread lain menghasilkan hasilnya, panggil resume() untuk melanjutkannya.
3. metode hasil(): hasil() menyebabkan thread melepaskan waktu CPU yang dialokasikan saat ini, namun tidak menyebabkan thread diblokir, yaitu thread masih dalam keadaan dapat dieksekusi dan dapat dialokasikan waktu CPU lagi di kapan pun. Efek pemanggilan yield() setara dengan penjadwal yang menganggap bahwa thread telah mengeksekusi cukup waktu untuk berpindah ke thread lain.
4. metode wait() dan notify(): Kedua metode ini digunakan bersama-sama. wait() menyebabkan thread memasuki status pemblokiran. Metode ini memiliki dua bentuk. Yang satu memungkinkan penentuan periode waktu dalam milidetik sebagai parameter, dan metode tersebut other tidak. Parameter, yang pertama akan masuk kembali ke status yang dapat dieksekusi ketika notify() yang sesuai dipanggil atau waktu yang ditentukan terlampaui, dan yang terakhir harus dipanggil ketika notify() yang sesuai dipanggil.
Sekilas, keduanya tampak tidak berbeda dengan pasangan metode suspend() dan resume(), namun sebenarnya keduanya sangat berbeda. Perbedaan intinya adalah bahwa semua metode yang dijelaskan di atas tidak akan melepaskan kunci yang ditempati (jika sudah terisi) saat memblokir, sedangkan aturan sebaliknya adalah kebalikannya.
Perbedaan inti di atas mengarah pada serangkaian perbedaan mendetail.
Pertama-tama, semua metode yang dijelaskan di atas termasuk dalam kelas Thread, tetapi pasangan ini milik langsung ke kelas Object, yaitu semua objek memiliki pasangan metode ini. Ini mungkin tampak luar biasa pada awalnya, tetapi sebenarnya ini sangat wajar, karena ketika pasangan metode ini diblokir, kunci yang ditempati harus dilepaskan, dan kunci tersebut dimiliki oleh objek apa pun. Memanggil metode wait() dari objek apa pun akan menyebabkan utas untuk diblokir dan kunci pada objek dilepaskan. Memanggil metode notify() dari objek apa pun akan menyebabkan thread yang dipilih secara acak diblokir dengan memanggil metode wait() dari objek tersebut untuk tidak diblokir (tetapi tidak akan dieksekusi sampai kunci diperoleh).
Kedua, semua metode yang dijelaskan di atas dapat dipanggil di lokasi mana pun, tetapi pasangan metode ini (wait() dan notify()) harus dipanggil dalam metode atau blok tersinkronisasi. Alasannya juga sangat sederhana, hanya dalam metode tersinkronisasi atau blok Hanya utas saat ini yang menempati kunci dan kunci dapat dilepaskan. Dengan cara yang sama, kunci pada objek yang memanggil pasangan metode ini harus dimiliki oleh thread saat ini, sehingga kunci tersebut dapat dilepaskan. Oleh karena itu, pasangan pemanggilan metode harus ditempatkan dalam metode atau blok tersinkronisasi yang objek terkuncinya adalah objek yang memanggil pasangan metode tersebut. Jika kondisi ini tidak terpenuhi, meskipun program masih dapat dikompilasi, pengecualian IllegalMonitorStateException akan terjadi saat runtime.
Karakteristik metode wait() dan notify() di atas menentukan bahwa metode tersebut sering digunakan bersama dengan metode atau blok yang disinkronkan. Membandingkannya dengan mekanisme komunikasi antar-proses pada sistem operasi akan mengungkapkan kesamaannya: metode atau blok yang disinkronkan memberikan Serupa untuk fungsi primitif sistem operasi, eksekusinya tidak akan diganggu oleh mekanisme multi-threading, dan pasangan ini setara dengan primitif blok dan bangun (pasangan metode ini dinyatakan tersinkronisasi). Kombinasi keduanya memungkinkan kita untuk mengimplementasikan serangkaian algoritma komunikasi antar-proses yang sangat indah pada sistem operasi (seperti algoritma semaphore), dan dapat digunakan untuk memecahkan berbagai masalah komunikasi antar-thread yang kompleks.
Dua poin terakhir tentang metode wait() dan notify():
Pertama: Thread yang tidak diblokir yang disebabkan oleh pemanggilan metode notify() dipilih secara acak dari thread yang diblokir dengan memanggil metode wait() pada objek. Kita tidak dapat memprediksi thread mana yang akan dipilih, jadi berhati-hatilah saat memprogram, untuk menghindarinya permasalahan yang timbul dari ketidakpastian ini.
Kedua: Selain notify(), ada juga metode notifyAll() yang juga dapat memainkan peran serupa. Satu-satunya perbedaan adalah memanggil metode notifyAll() akan menghapus semua thread yang diblokir dengan memanggil metode wait() dari objek sekaligus. Semua tidak diblokir. Tentu saja, hanya thread yang memperoleh kunci yang dapat memasuki status eksekusi.
Ketika berbicara tentang pemblokiran, kita harus berbicara tentang kebuntuan. Analisis singkat dapat mengungkapkan bahwa metode suspend() dan pemanggilan metode wait() tanpa menentukan periode waktu habis dapat menyebabkan kebuntuan. Sayangnya, Java tidak mendukung penghindaran kebuntuan pada tingkat bahasa, dan kita harus berhati-hati untuk menghindari kebuntuan dalam pemrograman.
Di atas kami telah menganalisis berbagai metode pemblokiran thread di Java. Kami fokus pada metode wait() dan notify() karena metode tersebut paling kuat dan paling fleksibel untuk digunakan, tetapi hal ini juga menyebabkan efisiensinya lebih rendah dan lebih rendah. lebih rawan kesalahan. Dalam penggunaan sebenarnya, kita harus menggunakan berbagai metode secara fleksibel untuk mencapai tujuan kita dengan lebih baik.
benang daemon
Utas daemon adalah jenis utas khusus. Perbedaan antara utas tersebut dan utas biasa adalah bahwa utas tersebut bukan bagian inti dari aplikasi. Ketika semua utas non-daemon suatu aplikasi dihentikan, meskipun masih ada utas daemon yang berjalan, aplikasi tersebut akan Terminasi, sebaliknya, aplikasi tidak akan berhenti selama masih ada thread non-daemon yang berjalan. Thread daemon umumnya digunakan untuk memberikan layanan ke thread lain di latar belakang.
Anda dapat menentukan apakah suatu thread merupakan thread daemon dengan memanggil metode isDaemon(), atau Anda dapat memanggil metode setDaemon() untuk menyetel thread sebagai thread daemon.
kelompok benang
Grup utas adalah konsep unik untuk Java. Di Java, grup utas adalah objek kelas ThreadGroup. Setiap utas termasuk dalam grup utas unik. Grup utas ini ditentukan saat utas dibuat dan tidak dapat digunakan selama seluruh siklus hidup benang. Anda dapat menentukan grup thread yang memiliki thread tersebut dengan memanggil konstruktor kelas Thread yang berisi parameter tipe ThreadGroup. Jika tidak ditentukan, default thread tersebut adalah grup thread sistem bernama sistem.
Di Java, semua grup thread harus dibuat secara eksplisit kecuali grup thread sistem yang sudah dibuat sebelumnya. Di Java, setiap grup thread, kecuali grup thread sistem, termasuk dalam grup thread lain. Anda dapat menentukan grup thread mana yang dimilikinya saat membuat grup thread. Dengan cara ini, semua grup thread membentuk pohon dengan grup thread sistem sebagai root.
Java memungkinkan kita untuk mengoperasikan semua thread dalam grup thread secara bersamaan. Misalnya, kita dapat mengatur prioritas semua thread di dalamnya dengan memanggil metode yang sesuai dari grup thread, dan kita juga dapat memulai atau memblokir semua thread di dalamnya. dia.
Peran penting lainnya dari mekanisme grup thread Java adalah keamanan thread. Mekanisme grup thread memungkinkan kami membedakan thread dengan karakteristik keamanan berbeda melalui pengelompokan, menangani thread dalam grup berbeda secara berbeda, dan mendukung penerapan langkah-langkah keamanan yang tidak setara melalui struktur hierarki grup thread. Kelas ThreadGroup Java menyediakan sejumlah besar metode untuk memudahkan kita mengoperasikan setiap grup thread di pohon grup thread dan setiap thread di grup thread.
Status Thread Pada titik waktu tertentu, thread hanya dapat berada dalam satu status.
BARU
Thread yang belum dimulai berada dalam kondisi ini.
DAPAT DIJALANKAN
Thread yang dieksekusi di mesin virtual Java berada dalam kondisi ini.
DIBLOKIR
Thread yang diblokir dan menunggu kunci monitor berada dalam kondisi ini.
MENUNGGU
Sebuah thread yang menunggu tanpa batas waktu hingga thread lain melakukan operasi tertentu berada dalam keadaan ini.
Status thread dari thread yang menunggu. Sebuah thread berada dalam status menunggu karena memanggil salah satu metode berikut:
Objek.tunggu tanpa nilai batas waktu
Thread.join tanpa nilai batas waktu
LockSupport.park
Sebuah thread dalam keadaan menunggu sedang menunggu thread lain untuk melakukan operasi tertentu. Misalnya, thread yang memanggil Object.wait() pada suatu objek sedang menunggu thread lain untuk memanggil Object.notify() atau Object.notifyAll() pada objek tersebut. Thread yang memanggil Thread.join() sedang menunggu thread yang ditentukan untuk dihentikan.
WAKTU_MENUNGGU
Sebuah thread yang menunggu thread lain untuk melakukan operasi yang bergantung pada waktu tunggu yang ditentukan berada dalam keadaan ini.
Status thread dari thread yang menunggu dengan waktu tunggu yang ditentukan. Sebuah thread berada dalam status tunggu berwaktu dengan memanggil salah satu metode berikut dengan waktu tunggu positif yang ditentukan:
Thread.tidur
Objek.tunggu dengan nilai batas waktu
Thread.join dengan nilai batas waktu
LockSupport.parkNanos
LockSupport.parkUntil
DIHENTIKAN
Thread yang keluar berada dalam kondisi ini.