Secara umum, kecepatan tugas produksi lebih besar dari kecepatan konsumsi. Pertanyaan detail adalah panjang antrian, dan bagaimana mencocokkan kecepatan produksi dan konsumsi.
Model produser-konsumen yang khas adalah sebagai berikut:
Untuk produksi umum lebih cepat dari konsumsi. Ketika antrian penuh, kami tidak ingin tugas apa pun diabaikan atau tidak dieksekusi. . Terus mengirimkan tugas saat antrian tidak penuh, jadi tidak ada waktu idle yang terbuang. Blocking juga mudah.
Lebih lanjut, ketika antriannya kosong, konsumen tidak bisa mendapatkan tugas, mereka bisa menunggu sebentar sebelum mendapatkannya. . Disarankan untuk menelepon. Dengan cara ini, ketika produser benar -benar berhenti produksi, konsumen tidak akan menunggu tanpa batas.
Oleh karena itu, model produksi dan konsumsi yang efisien diimplementasikan.
Tunggu, karena JUC telah membantu kami menerapkan kumpulan utas, mengapa kami masih perlu menggunakan rangkaian hal ini? Bukankah lebih nyaman untuk menggunakan ExecutorService secara langsung?
Mari kita lihat struktur dasar ThreadPoolExecutor:
Tetapi masalahnya adalah bahwa bahkan jika Anda secara manual menentukan blockingqueue sebagai implementasi antrian saat membangun threadpoolexecutor, pada kenyataannya, ketika antrian penuh, metode eksekusi tidak akan memblokir.
Salinan kode adalah sebagai berikut:
public void execute (runnable command) {
if (command == null)
lempar nullpointerexception baru ();
if (poolsize> = corePoolSize ||! addifundercorepoolsize (command)) {
if (runstate == running && workqueue.offer (command)) {
if (runstate! = running || poolsize == 0)
PasteQueuedTaskHandled (Command);
}
lain jika (! addifunderbuiMaximumpoolSize (command)))
tolak (perintah); // shutdown atau jenuh
}
}
Pada saat ini, sesuatu perlu dilakukan untuk mencapai hasil: ketika produsen mengirimkan tugas dan antrian penuh, produser dapat memblokirnya dan menunggu tugas dikonsumsi.
Kuncinya adalah bahwa dalam lingkungan yang bersamaan, antrian penuh tidak dapat dinilai oleh produser, dan threadpoolexecutor.getqueue (). Size () tidak dapat dipanggil untuk menentukan apakah antrian penuh.
Dalam implementasi Pool Thread, ketika antrian penuh, RejectExecutionHandler masuk selama konstruksi akan dipanggil untuk menolak pemrosesan tugas. Implementasi default adalah abortpolicy, yang secara langsung melempar ExedExecutionException.
Saya tidak akan membahas beberapa strategi penolakan di sini. Konsumsi.
Salinan kode adalah sebagai berikut:
kelas statis public callerrunspolicy implement ditolak executionHandler {
/**
* Membuat <tt> callerrunspolicy </tt>.
*/
callerrunspolicy publik () {}
/**
* Mengeksekusi tugas r di utas penelepon, kecuali jika pelaksana
* telah ditutup, dalam hal ini tugas dibuang.
* @param r Tugas runnable diminta untuk dieksekusi
* @param e Pelaksana yang mencoba menjalankan tugas ini
*/
public void ditolak
if (! e.isshutdown ()) {
r.run ();
}
}
}
Namun, strategi ini juga memiliki bahaya tersembunyi. Terus menghasilkan tugas.
Mengacu pada ide -ide serupa, cara paling sederhana, kita dapat secara langsung mendefinisikan penonton yang ditolak, dan mengubahnya menjadi panggilan blockingqueue.Put saat antrian penuh untuk mewujudkan pemblokiran produser:
Salinan kode adalah sebagai berikut:
new RejectExecutionHandler () {
@Mengesampingkan
public void ditolak
if (! executor.isshutdown ()) {
mencoba {
Executor.getQueue (). Put (r);
} catch (InterruptedException e) {
// tidak boleh terganggu
}
}
}
};
Dengan cara ini, kita tidak perlu lagi peduli dengan logika antrian dan konsumen.
Dibandingkan dengan desain asli, metode ini dapat mengurangi jumlah kode, dan dapat menghindari banyak masalah di lingkungan bersamaan. Tentu saja, Anda juga dapat menggunakan cara lain, seperti menggunakan semafor sebagai batas masuk saat mengirimkan, tetapi jika Anda hanya ingin memblokir produser, itu akan tampak rumit.