2. Pendahuluan
Teknologi multithreading terutama memecahkan masalah eksekusi beberapa utas di unit prosesor. Ini dapat secara signifikan mengurangi waktu idle dari unit prosesor dan meningkatkan kemampuan throughput unit prosesor. Namun, overhead pembuatan utas yang sering sangat besar. Jadi bagaimana mengurangi bagian overhead ini, Anda perlu mempertimbangkan untuk menggunakan kumpulan utas. Kumpulan utas adalah wadah utas, yang hanya menjalankan jumlah utas yang dinilai sekaligus. Kumpulan utas digunakan untuk mengelola jumlah utas yang dinilai ini.
3. Diagram struktur kelas yang melibatkan kumpulan benang
Di antara mereka, yang utama untuk kami gunakan adalah kelas ThreadPoolExecutor.
4. Cara membuat kumpulan utas
Kami umumnya memiliki metode berikut untuk membuat kumpulan utas:
1. Gunakan Kelas Pabrik Pelaksana
Pelaksana terutama menyediakan metode berikut untuk membuat kumpulan utas:
Mari kita lihat contoh penggunaan di bawah ini:
1) NewfixedThreadPool (Fixed Thread Pool)
Public Class FixThreadPool {public static void main (string [] args) {executorService pool = executors.newfixedThreadpool (5); // Buat kumpulan utas dengan ukuran tetap 5 untuk (int i = 0; i <10; i ++) {pool.submit (mytread baru ()); } pool.shutdown (); }} kelas publik MyThread memperluas thread {@Override public void run () {System.out.println (thread.currentThread (). getName () + "eksekusi ..."); }}Hasil tes adalah sebagai berikut:
Pool-1-Thread-1 mengeksekusi. . .
Pool-1-Thread-2 mengeksekusi. . .
Pool-1-Thread-3 mengeksekusi. . .
Pool-1-Thread-2 mengeksekusi. . .
Pool-1-Thread-3 mengeksekusi. . .
Pool-1-Thread-2 mengeksekusi. . .
Pool-1-Thread-2 mengeksekusi. . .
Pool-1-Thread-3 mengeksekusi. . .
Pool-1-Thread-5 sedang dieksekusi. . .
Pool-1-Thread-4 mengeksekusi. . .
Kolam Benang Ukuran Tetap: Buat utas setiap kali tugas dikirimkan, sampai utas mencapai ukuran maksimum kumpulan utas. Ukuran kumpulan benang akan tetap tidak berubah setelah mencapai nilai maksimumnya. Jika utas berakhir karena pengecualian eksekusi, kumpulan utas akan menambahkan utas baru.
2) NewsingLetHreadExecutor (Single Thread Pool)
Public Class singlethreadpool {public static void main (string [] args) {executorService pool = executors.newsinglethreadExecutor (); // Buat kumpulan utas tunggal untuk (int i = 0; i <100; i ++) {pool.submit (myThread baru ()); } pool.shutdown (); }}Hasil tes adalah sebagai berikut:
Pool-1-Thread-1 mengeksekusi. . .
Pool-1-Thread-1 mengeksekusi. . .
Pool-1-Thread-1 mengeksekusi. . .
Pool-1-Thread-1 mengeksekusi. . .
Pool-1-Thread-1 mengeksekusi. . .
Pool-1-Thread-1 mengeksekusi. . .
Pool-1-Thread-1 mengeksekusi. . .
Pool-1-Thread-1 mengeksekusi. . .
Pool-1-Thread-1 mengeksekusi. . .
Pool-1-Thread-1 mengeksekusi. . .
Kolam benang tunggal: kumpulan utas ini hanya memiliki satu utas yang berfungsi, yang berarti bahwa eksekusi serial tunggal dari semua tugas. Jika utas unik ini berakhir karena pengecualian, maka akan ada utas baru untuk menggantinya. Kumpulan utas ini memastikan bahwa perintah eksekusi semua tugas dieksekusi dalam urutan pengajuan tugas.
3) NewsCheduledThreadPool
kelas publik StorduledThreadPool {public static void main (string [] args) {scheduledExecutorService pool = executors.newscheduledThreadpool (6); untuk (int i = 0; i <10000; i ++) {pool.submit (myThread baru ()); } pool.schedule (mythread baru (), 1000, timeunit.milliseconds); pool.schedule (mythread baru (), 1000, timeunit.milliseconds); pool.shutdown (); }}Hasil tes adalah sebagai berikut:
Pool-1-Thread-1 mengeksekusi. . .
Pool-1-Thread-6 mengeksekusi. . .
Pool-1-Thread-5 sedang dieksekusi. . .
Pool-1-Thread-4 mengeksekusi. . .
Pool-1-Thread-2 mengeksekusi. . .
Pool-1-Thread-3 mengeksekusi. . .
Pool-1-Thread-4 mengeksekusi. . .
Pool-1-Thread-5 sedang dieksekusi. . .
Pool-1-Thread-6 mengeksekusi. . .
Pool-1-Thread-1 mengeksekusi. . .
…………………………………………………………………………………………………………………………………………………………………………… ……………………………………………………………………………………………………………………………………………………………………………
Pool-1-Thread-4 mengeksekusi. . .
Pool-1-Thread-1 mengeksekusi. . .
Dua utas terakhir dari hasil tes hanya mulai mengeksekusi setelah menunda 1s. Kumpulan utas ini mendukung persyaratan waktu dan pelaksanaan tugas berkala
4) NewcachedThreadPool (Pool Thread yang Dapat Dicat)
Public Class CachedThreadPool {public static void main (string [] args) {executorService pool = executors.newcachedThreadpool (); untuk (int i = 0; i <100; i ++) {pool.submit (myThread baru ()); } pool.shutdown (); }}Hasil tes adalah sebagai berikut:
Pool-1-Thread-5 sedang dieksekusi. . .
Pool-1-Thread-7 mengeksekusi. . .
Pool-1-Thread-5 sedang dieksekusi. . .
Pool-1-thread-16 sedang dalam pelaksanaan. . .
Pool-1-Thread-17 sedang dieksekusi. . .
Pool-1-thread-16 sedang dalam pelaksanaan. . .
Pool-1-Thread-5 sedang dieksekusi. . .
Pool-1-Thread-7 mengeksekusi. . .
Pool-1-thread-16 sedang dalam pelaksanaan. . .
Pool-1-Thread-18 mengeksekusi. . .
Pool-1-Thread-10 mengeksekusi. . .
Pool Thread Cachable: Jika ukuran kumpulan utas melebihi utas yang diperlukan untuk memproses tugas, beberapa benang idle (tidak ada eksekusi tugas dalam 60 detik) akan didaur ulang. Ketika jumlah tugas meningkat, kumpulan utas ini dapat dengan cerdas menambahkan utas baru untuk menangani tugas. Kumpulan utas ini tidak membatasi ukuran kumpulan utas, yang sepenuhnya tergantung pada ukuran utas maksimum yang dapat dibuat oleh sistem operasi (atau JVM).
Pejabat itu menyarankan agar programmer menggunakan metode pelaksana pabrik yang lebih nyaman. Kumpulan utas ini telah ditentukan sebelumnya dengan konfigurasi default untuk sebagian besar skenario penggunaan.
2. Mewariskan kelas ThreadPoolExecutor dan salin metode konstruktor kelas induk.
Sebelum memperkenalkan metode ini, mari kita analisis beberapa kode yang mendasari sebelumnya untuk membuat kumpulan utas?
Eksekutor kelas publik {Public ExecutorService newfixedThreadPool (int nthreads) {return new ThreadPoolExecutor (nthreads, nthreads, 0L, timeunit.milliseconds, LinkedBlockequeue baru <Runnable> ()); } public Static ExecutorService NewsingLetHreadExecutor () {return baru finalzableDelegatedExecutorService (ThreadPoolExecutor baru (1, 1, 0l, timeunit.milliseconds, baru LinkedBlockingQueue <Runnable> ()))); }}Dari kode yang mendasari kelas Executors Factory, kita dapat melihat bahwa metode yang disediakan oleh kelas pabrik untuk membuat kumpulan utas sebenarnya diimplementasikan dengan membangun ThreadPoolExecutor. Kode Metode Konstruktor ThreadPoolExecutor adalah sebagai berikut:
Thread Public PublicExecutor (int corepoolsize, int maximumpoolsize, long stexalivetime, timeUnit unit, blockingqueue <runnable> workqueue, threadfactory threadFactory, ditolak executionHandler handler) {if (corepoolsize <0 || maximumpensize <= 0 || IlegalargumentException (); if (workqueue == null || threadFactory == null || handler == null) lempar nullpointerException baru (); this.corepoolsize = corePoolsize; this.maximumpoolSize = maximumpoolSize; this.workqueue = workqueue; this.eepalivetime = unit.tonanos (Keepalivetime); this.threadFactory = threadFactory; this.handler = handler; }Kemudian, mari kita bicara tentang metode konstruktor ThreadPoolExecutor. Dalam metode konstruksi ini, terutama ada parameter berikut:
CorePoolsize-Jumlah utas yang disimpan di kolam, termasuk utas gratis.
MaximumpoolSize - Jumlah maksimum utas yang diizinkan di kolam.
Keepalivetime-Ketika jumlah utas lebih besar dari corePoolsize, ini adalah waktu terlama bagi utas idle untuk menunggu tugas baru.
Unit-- Keepalivetime Parameter Time Unit.
WORKQUEUE-Antrian yang digunakan untuk menjaga tugas sebelum dieksekusi. Antrian ini hanya mempertahankan tugas runnable yang diajukan dengan metode eksekusi.
ThreadFactory-Pabrik yang digunakan oleh pelaksana untuk membuat utas baru.
Handler-pawang yang digunakan saat eksekusi diblokir karena ruang lingkup benang dan kapasitas antrian.
Selanjutnya, mari kita bicara tentang hubungan antara parameter ini. Ketika kumpulan utas baru saja dibuat, tidak ada utas di kumpulan utas (perhatikan bahwa bukan bahwa sejumlah utas dibuat segera setelah kumpulan utas dibuat). Ketika metode execute () dipanggil untuk menambahkan tugas, kumpulan utas akan membuat penilaian berikut:
1) Jika jumlah utas yang saat ini berjalan kurang dari corePoolsize, maka buat utas baru segera untuk melakukan tugas ini.
2) Jika jumlah utas yang saat ini berjalan lebih besar dari atau sama dengan corePoolsize, maka tugas ini akan dimasukkan ke dalam antrian.
3) Jika antrian kumpulan utas penuh, tetapi jumlah utas yang berjalan kurang dari MaximumpoolSize, utas baru masih akan dibuat untuk melakukan tugas ini.
4) Jika antrian penuh dan jumlah utas yang sedang berjalan lebih besar dari atau sama dengan MaximumpoolSize, kumpulan utas akan menangani tugas saat ini berdasarkan kebijakan penolakan.
5) Ketika tugas dieksekusi, utas akan mengambil tugas berikutnya dari antrian untuk dieksekusi. Jika tidak ada tugas untuk dieksekusi dalam antrian, maka utas akan menganggur. Jika waktu kelangsungan hidup Keepalivetime melebihi, utas akan didaur ulang oleh kumpulan utas (Catatan: Benang daur ulang bersyarat. Jika jumlah utas yang saat ini berjalan lebih besar dari CorePoolsize, utas tersebut akan dihancurkan. Jika tidak lebih besar dari corePools, utas tidak akan dihancurkan. Jumlah utas harus disimpan di dalam jumlah corePools). Mengapa bukan karena utas itu didaur ulang segera setelah menganggur, tetapi perlu menunggu sampai melebihi kiper sebelum utas didaur ulang? Alasannya sangat sederhana: karena penciptaan dan penghancuran utas banyak berkonsumsi, dan itu tidak dapat sering dibuat dan dihancurkan. Setelah melampaui Keepalivetime, ditemukan bahwa utas ini memang tidak digunakan, dan itu akan dihancurkan. Dalam hal ini, unit mewakili unit waktu Keepalivetime, dan definisi unit adalah sebagai berikut:
public enum TimeUnit { NANOSECONDS { // keepAliveTime in nanoseconds}, MICROSECONDS { // keepAliveTime in microseconds}, MILLISECONDS { // keepAliveTime in milliseconds}, SECONDS { // keepAliveTime in seconds}, MINUTES { // keepAliveTime in minutes}, HOURS { // Keepalivetime in Hours}, Days {// Keepalivetime in Days}; Mari kita analisis kode sumber di bawah ini. Untuk situasi di atas, kode sumber terutama terlibat adalah sebagai berikut:
private boolean addifundercorepoolSize (runnable firstTask) {thread t = null; final reentrantlock mainlock = this.mainlock; mainlock.lock (); coba {if (poolsize <corepoolsize && runstate == running) t = addThread (firstTask); } akhirnya {mainlock.unlock (); } if (t == null) return false; t.start (); Kembali Benar; } Bahkan, kode ini sangat sederhana. Ini terutama menjelaskan bahwa jika kumpulan utas saat ini lebih kecil dari corePoolsize, utas baru dibuat untuk menangani tugas.
private boolean addifundermaximumpoolSize (runnable firstTask) {thread t = null; final reentrantlock mainlock = this.mainlock; mainlock.lock (); coba {if (poolsize <maximumpoolSize && runstate == running) t = addThread (firstTask); } akhirnya {mainlock.unlock (); } if (t == null) return false; t.start (); Kembali Benar; }Kode di atas menjelaskan bahwa jika jumlah kumpulan utas saat ini kurang dari MaximumpoolSize, utas akan dibuat untuk menjalankan tugas.
5. Antrian Pool Thread
Ada 3 jenis antrian kumpulan utas:
Direct Commit: Opsi default untuk antrian kerja adalah synchronousqueue, yang mengirimkan tugas langsung ke utas tanpa menyimpannya. Di sini, jika tidak ada utas yang tersedia untuk menjalankan tugas segera, mencoba mengantri tugas akan gagal, sehingga membangun utas baru. Kebijakan ini menghindari kunci saat menangani set permintaan yang mungkin memiliki dependensi internal. Pengajuan langsung biasanya memerlukan maksimumpoolsizes tak terbatas untuk menghindari penolakan tugas yang baru diajukan. Strategi ini memungkinkan utas yang tidak terikat untuk memiliki kemungkinan pertumbuhan ketika perintah tiba terus menerus dengan rata -rata yang dapat ditangani antrian.
Antrian Tidak Terbatas: Menggunakan antrian tak terbatas (misalnya, LinkedBlockingQueue tanpa kapasitas yang telah ditentukan) akan menyebabkan tugas -tugas baru menunggu dalam antrian ketika semua utas corePoolsize sibuk. Dengan cara ini, utas yang dibuat tidak akan melebihi corePoolsize. (Nilai MaximumpoolSize tidak valid.) Ketika setiap tugas sepenuhnya independen dari tugas -tugas lain, yaitu, eksekusi tugas tidak saling mempengaruhi, itu cocok untuk antrian yang tidak terbatas; Misalnya, di server halaman web. Antrian ini dapat digunakan untuk menangani permintaan ledakan sementara, dan strategi ini memungkinkan utas yang tidak terikat untuk memiliki kemungkinan pertumbuhan ketika perintah tiba terus menerus melebihi rata -rata yang dapat ditangani antrian.
Antrian Terikat: Saat menggunakan maximumpoolsizes terbatas, antrian terikat (seperti arrayblockingqueue) membantu mencegah kelelahan sumber daya, tetapi mungkin sulit untuk disesuaikan dan dikendalikan. Ukuran antrian dan ukuran kolam maksimum mungkin perlu saling menukar: menggunakan antrian besar dan kolam kecil dapat meminimalkan penggunaan CPU, sumber daya sistem operasi, dan pengalihan konteks overhead, tetapi dapat menghasilkan pengurangan manual dalam throughput. Jika tugas sering diblokir (misalnya, jika itu adalah batas I/O), sistem dapat menjadwalkan waktu untuk lebih banyak utas daripada yang Anda izinkan. Menggunakan antrian kecil biasanya membutuhkan ukuran kolam yang lebih besar dan memiliki penggunaan CPU yang lebih tinggi, tetapi mungkin menghadapi overhead penjadwalan yang tidak dapat diterima, yang juga dapat mengurangi throughput.
Mari kita bicara tentang antrian kumpulan utas di bawah ini, diagram struktur kelas adalah sebagai berikut:
1) Synchronousqueue
Antrian sesuai dengan pengiriman langsung yang disebutkan di atas. Pertama -tama, Synchronousqueue tidak terikat, yang berarti kemampuannya untuk menyimpan angka tidak terbatas. Namun, karena karakteristik antrian itu sendiri, setelah menambahkan elemen, Anda harus menunggu utas lain untuk mengambilnya sebelum Anda dapat terus menambahkannya.
2) LinkedBlockingQueue
Antrian sesuai dengan antrian yang tidak terikat di atas.
3) ArrayBlockingQueue
Antrian sesuai dengan antrian terikat di atas. ArrayBlockingQueue memiliki 3 konstruktor berikut:
Public ArrayBlockingQueue (Kapasitas int) {this (kapasitas, false); } public arrayblockingqueue (kapasitas int, boolean fair) {if (kapasitas <= 0) melempar IllegalArgumentException baru (); this.items = (e []) objek baru [kapasitas]; lock = baru reentrantlock (adil); notempty = lock.newcondition (); THIFFULL = LOCK.NEWCONDITION (); } public arrayblockingqueue (kapasitas int, boolean fair, collection <? extends e> c) {this (kapasitas, adil); if (kapasitas <c.size ()) melempar IllegalArgumentException baru (); untuk (iterator <? Extends e> it = c.iterator (); it.hasnext ();) add (it.next ()); }Mari fokus pada pameran ini. Fair mewakili strategi kompetisi utas akses antrian. Ketika benar, antrian penyisipan tugas mematuhi aturan FIFO. Jika salah, Anda dapat "memotong antrian". Misalnya, jika ada banyak tugas yang antri sekarang, utas telah menyelesaikan tugas dan tugas baru datang. Jika salah, tugas ini tidak perlu antri dalam antrian. Anda dapat secara langsung memotong antrian dan kemudian menjalankannya. Seperti yang ditunjukkan pada gambar di bawah ini:
6. Strategi Eksekusi Penolakan Kumpulan Thread
Ketika jumlah utas mencapai nilai maksimum, tugas masih datang saat ini, dan saat ini, saya harus menolak untuk menerima tugas.
ThreadPoolExecutor memungkinkan penyesuaian kebijakan eksekusi saat menambahkan tugas gagal. Anda dapat menghubungi metode setReTEDExecutionHandler () dari kumpulan utas dan mengganti kebijakan yang ada dengan objek RejectEdexecutionHandler yang disesuaikan. Strategi pemrosesan default yang disediakan oleh ThreadPoolExecutor adalah untuk secara langsung membuang dan melemparkan informasi pengecualian secara bersamaan. ThreadPoolExecutor menyediakan 4 kebijakan yang ada, yaitu:
ThreadPoolExecutor.AbortPolicy: Menunjukkan bahwa tugas ditolak dan pengecualian dilemparkan. Kode sumber adalah sebagai berikut:
Public Static Class AbortPolicy Implements RejectExecutionHandler { /*** Membuat <tt> abortpolicy </tt>. * / public abortpolicy () {} / *** Selalu melempar RejectExecutionException. * @param r Tugas runnable yang diminta untuk dieksekusi * @param e Eksekutor yang mencoba menjalankan tugas ini * @throws RejectExecutionException selalu. */ public void ditolak // pengecualian lemparan}}ThreadPoolExecutor.DiscardPolicy: Ini berarti tugas ditolak tetapi tidak ada tindakan yang dilakukan. Kode sumber adalah sebagai berikut:
Kelas Statis Publik DiscardPolicy Implements RejectExecutionHandler { /*** Membuat <TT> DiscardPolicy </tt>. * / DiscardPolicy publik () {} / *** tidak melakukan apa pun, yang memiliki efek pembuangan tugas r. * @param r Tugas runnable yang diminta untuk dieksekusi * @param e Pelaksana yang mencoba melaksanakan tugas ini */ public void ditolakThreadPoolExecutor.CallerRunSpolicy: Menunjukkan bahwa tugas ditolak dan tugas tersebut dieksekusi secara langsung di utas penelepon. Kode sumber adalah sebagai berikut:
Kelas Statis Publik CallerRunSpolicy Implements RejectExecutionHandler { /*** Membuat <tt> callerrunspolicy </tt>. * / Public callerrunspolicy () {} / ** * Menjalankan 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 // Jalankan tugas secara langsung}}}ThreadPoolExecutor.DiscardoldestPolicy: Ini berarti bahwa tugas pertama dalam antrian tugas dibuang terlebih dahulu, dan kemudian tugas ditambahkan ke antrian. Kode sumber adalah sebagai berikut:
Kelas Statis Publik DiscardoldestPolicy Implements RejectExecutionHandler { /*** Membuat <Tt> DiscardoldestPolicy </tt> untuk pelaksana yang diberikan. */ public DiscardoldestPolicy () {} public void ditolak // Buang tugas pertama dalam antrian E.Execute (r); // Jalankan tugas baru}}}Ketika tugas tiba terus menerus, tugas akan disurvei dari antrian dan kemudian tugas baru akan dieksekusi.
Meringkaskan
Di atas adalah penjelasan terperinci dari kumpulan utas JDK sendiri yang diperkenalkan oleh editor. Saya harap ini akan membantu semua orang. Jika Anda memiliki pertanyaan, silakan tinggalkan saya pesan dan editor akan membalas semua orang tepat waktu. Terima kasih banyak atas dukungan Anda ke situs web Wulin.com!