Tampaknya mustahil untuk membuat kumpulan thread dalam cache dengan batasan jumlah thread yang dapat dibuat.
Berikut adalah bagaimana static Executors.newCachedThreadPool diimplementasikan di perpustakaan Java standar:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
Jadi, gunakan templat itu untuk terus membuat kumpulan utas cache berukuran tetap:
new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new SynchronusQueue<Runable>());
Sekarang jika Anda menggunakan ini dan mengirimkan 3 tugas, semuanya akan baik-baik saja. Menyerahkan tugas lebih lanjut akan menghasilkan pengecualian eksekusi yang ditolak.
Mencoba ini:
new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runable>());
Akan menghasilkan semua utas mengeksekusi berurutan. Yaitu, kumpulan utas tidak akan pernah membuat lebih dari satu utas untuk menangani tugas Anda.
Ini adalah bug dalam metode eksekusi ThreadPoolExecutor? Atau mungkin ini disengaja? Atau ada cara lain?
Sunting: Saya ingin sesuatu persis seperti kumpulan thread yang di-cache (membuat thread berdasarkan permintaan dan kemudian membunuhnya setelah beberapa waktu habis) tetapi dengan batas jumlah thread yang dapat dibuat dan kemampuan untuk terus mengantre tugas-tugas tambahan setelah memiliki tekan batas utasnya. Menurut tanggapan Sjlee, ini tidak mungkin. Melihat metode execute () dari ThreadPoolExecutor memang tidak mungkin. Saya perlu subclass ThreadPoolExecutor dan menimpa mengeksekusi () agak seperti SwingWorker tidak, tetapi apa yang dilakukan SwingWorker dalam mengeksekusi () adalah hack lengkap.
sumber
Jawaban:
ThreadPoolExecutor memiliki beberapa perilaku utama berikut, dan masalah Anda dapat dijelaskan oleh perilaku ini.
Ketika tugas diserahkan,
Dalam contoh pertama, perhatikan bahwa SynchronousQueue pada dasarnya memiliki ukuran 0. Oleh karena itu, saat Anda mencapai ukuran maksimal (3), kebijakan penolakan akan muncul di (# 4).
Pada contoh kedua, antrian pilihan adalah LinkedBlockingQueue yang memiliki ukuran tidak terbatas. Karena itu, Anda terjebak dengan perilaku # 2.
Anda tidak dapat benar-benar bermain-main dengan tipe cache atau tipe tetap, karena perilaku mereka hampir sepenuhnya ditentukan.
Jika Anda ingin memiliki kumpulan ulir yang dibatasi dan dinamis, Anda perlu menggunakan ukuran inti positif dan ukuran maks dikombinasikan dengan antrian ukuran terbatas. Sebagai contoh,
Tambahan : ini adalah jawaban yang cukup lama, dan tampaknya JDK mengubah perilakunya ketika mengenai ukuran inti 0. Sejak JDK 1.6, jika ukuran inti adalah 0 dan kumpulan tidak memiliki utas, ThreadPoolExecutor akan menambahkan utas untuk menjalankan tugas itu. Oleh karena itu, ukuran inti 0 adalah pengecualian dari aturan di atas. Terima kasih Steve untuk membawa yang menjadi perhatian saya.
sumber
allowCoreThreadTimeOut
untuk membuat jawaban ini sempurna. Lihat jawaban @ user1046052Kecuali saya melewatkan sesuatu, solusi untuk pertanyaan awal itu sederhana. Kode berikut mengimplementasikan perilaku yang diinginkan seperti yang dijelaskan oleh poster asli. Ini akan memunculkan hingga 5 utas untuk bekerja pada antrian tanpa batas dan utas menganggur akan berakhir setelah 60 detik.
sumber
Punya masalah yang sama. Karena tidak ada jawaban lain yang menyatukan semua masalah, saya menambahkan milik saya:
Sekarang ditulis dengan jelas dalam dokumen : Jika Anda menggunakan antrian yang tidak memblokir (
LinkedBlockingQueue
) pengaturan utas maksimum tidak berpengaruh, hanya utas inti yang digunakan.begitu:
Pelaksana ini memiliki:
Tidak ada konsep utas maksimal karena kami menggunakan antrian tanpa batas. Ini adalah hal yang baik karena antrian tersebut dapat menyebabkan pelaksana membuat sejumlah besar utas tambahan non-inti jika mengikuti kebijakan yang biasa.
Antrian ukuran maks
Integer.MAX_VALUE
.Submit()
akan melemparRejectedExecutionException
jika jumlah tugas yang tertunda melebihiInteger.MAX_VALUE
. Tidak yakin kami akan kehabisan memori terlebih dahulu atau ini akan terjadi.Memiliki 4 utas inti yang mungkin. Thread inti idle secara otomatis keluar jika idle selama 5 detik
setThreads()
.Memastikan jumlah minimum utas inti tidak pernah kurang dari satu, atau yang lainnya
submit()
akan menolak setiap tugas. Karena utas inti harus> = utas maks, metode ini jugasetThreads()
menetapkan utas maksimum, meskipun pengaturan utas maksimum tidak berguna untuk antrian tanpa batas.sumber
Dalam contoh pertama Anda, tugas berikutnya ditolak karena
AbortPolicy
ini adalah defaultRejectedExecutionHandler
. ThreadPoolExecutor berisi kebijakan berikut, yang dapat Anda ubah melaluisetRejectedExecutionHandler
metode:Kedengarannya seperti Anda ingin kolam thread di-cache dengan CallerRunsPolicy.
sumber
Tidak ada jawaban di sini yang memperbaiki masalah saya, yang berkaitan dengan membuat koneksi HTTP dalam jumlah terbatas menggunakan klien HTTP Apache (versi 3.x). Karena saya butuh beberapa jam untuk mencari tahu pengaturan yang bagus, saya akan membagikan:
Ini menciptakan
ThreadPoolExecutor
yang dimulai dengan lima dan memegang maksimal sepuluh utas yang berjalan secara simultanCallerRunsPolicy
untuk mengeksekusi.sumber
Per Javadoc untuk ThreadPoolExecutor:
(Penekanan milikku.)
Jawaban jitter adalah apa yang Anda inginkan, meskipun jawaban saya menjawab pertanyaan Anda yang lain. :)
sumber
ada satu opsi lagi. Alih-alih menggunakan SynchronousQueue baru Anda juga dapat menggunakan antrian lain, tetapi Anda harus memastikan ukurannya adalah 1, sehingga akan memaksa layanan executorser untuk membuat utas baru.
sumber
Tidak terlihat seolah-olah salah satu jawaban benar-benar menjawab pertanyaan - pada kenyataannya saya tidak bisa melihat cara melakukan ini - bahkan jika Anda subkelas dari PooledExecutorService karena banyak metode / properti bersifat pribadi misalnya membuat addIfUnderMaximumPoolSize dilindungi Anda bisa lakukan hal berikut:
Yang paling dekat yang saya dapatkan adalah ini - tetapi bahkan itu bukan solusi yang sangat baik
ps tidak menguji di atas
sumber
Ini solusi lain. Saya pikir solusi ini berperilaku seperti yang Anda inginkan (meskipun tidak bangga dengan solusi ini):
sumber
Ini yang Anda inginkan (setidaknya saya kira begitu). Untuk penjelasan, periksa jawaban Jonathan Feinberg
Executors.newFixedThreadPool(int n)
sumber
Anda dapat menggunakan
ThreadPoolExecutor
seperti yang disarankan oleh @sjleeAnda dapat mengontrol ukuran kolam secara dinamis. Lihat pertanyaan ini untuk lebih jelasnya:
Pool Thread Dinamis
ATAU
Anda dapat menggunakan newWorkStealingPool API, yang telah diperkenalkan dengan java 8.
Secara default, level paralelisme diatur ke jumlah inti CPU di server Anda. Jika Anda memiliki 4 server CPU inti, ukuran kumpulan thread akan menjadi 4. API ini mengembalikan
ForkJoinPool
tipeExecutorService
dan memungkinkan pencurian pekerjaan dari thread yang menganggur dengan mencuri tugas dari utas yang sibuk di ForkJoinPool.sumber
Masalahnya diringkas sebagai berikut:
Sebelum menunjuk ke solusi saya akan menjelaskan mengapa solusi berikut tidak bekerja:
Ini tidak akan mengantri tugas apa pun ketika batas 3 tercapai karena SynchronousQueue, menurut definisi, tidak dapat menampung elemen apa pun.
Ini tidak akan membuat lebih dari satu utas karena ThreadPoolExecutor hanya membuat utas yang melebihi corePoolSize jika antrian penuh. Tapi LinkedBlockingQueue tidak pernah penuh.
Ini tidak akan menggunakan kembali utas sampai corePoolSize telah tercapai karena ThreadPoolExecutor menambah jumlah utas sampai corePoolSize tercapai bahkan jika utas yang ada menganggur. Jika Anda dapat hidup dengan kekurangan ini maka ini adalah solusi termudah untuk masalah ini. Ini juga merupakan solusi yang dijelaskan dalam "Java Concurrency in Practice" (catatan kaki pada hal. 175).
Satu-satunya solusi lengkap untuk masalah yang dideskripsikan tampaknya adalah yang melibatkan menimpa metode antrian
offer
dan menulisRejectedExecutionHandler
seperti yang dijelaskan dalam jawaban atas pertanyaan ini: Bagaimana cara mendapatkan ThreadPoolExecutor untuk meningkatkan utas secara maksimal sebelum mengantri?sumber
Ini berfungsi untuk Java8 + (dan lainnya, untuk saat ini ..)
di mana 3 adalah batas jumlah utas, dan 5 adalah batas waktu untuk utas menganggur.
Jika Anda ingin memeriksa apakah itu berfungsi sendiri , berikut adalah kode untuk melakukan pekerjaan:
Output dari kode di atas untuk saya adalah
sumber