Apa sebenarnya perbedaan antara ukuran kolam inti dan ukuran kolam maksimum saat kita membicarakannya ThreadPoolExecutor
?
Bisakah itu dijelaskan dengan bantuan contoh?
Apa sebenarnya perbedaan antara ukuran kolam inti dan ukuran kolam maksimum saat kita membicarakannya ThreadPoolExecutor
?
Bisakah itu dijelaskan dengan bantuan contoh?
Jawaban:
Dari postingan blog ini :
Ambil contoh ini. Ukuran kumpulan utas awal adalah 1, ukuran kumpulan inti adalah 5, ukuran kumpulan maksimum adalah 10 dan antrian adalah 100.
Saat permintaan masuk, utas akan dibuat hingga 5 dan kemudian tugas akan ditambahkan ke antrian hingga mencapai 100. Saat antrian penuh, utas baru akan dibuat hingga
maxPoolSize
. Setelah semua utas digunakan dan antrian selesai, tugas akan ditolak. Saat antrian berkurang, begitu pula jumlah utas aktif.
allowCoreThreadTimeOut(boolean)
yang memungkinkan benang inti untuk dibunuh setelah diberikan waktu idle. Menyetel ini ke true dan setelan core threads
= max threads
memungkinkan kumpulan utas berskala antara 0 dan max threads
.
JIKA menjalankan utas> corePoolSize & <maxPoolSize , maka buat Thread baru jika antrian tugas Total penuh dan yang baru tiba.
Formulir doc: (Jika ada lebih dari corePoolSize tetapi kurang dari maximumPoolSize utas yang berjalan, utas baru akan dibuat hanya jika antrian penuh.)
Sekarang, ambil contoh sederhana,
ThreadPoolExecutor executorPool = new ThreadPoolExecutor(5, 10, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(50));
Di sini, 5 adalah corePoolSize - artinya Jvm akan membuat utas baru untuk tugas baru untuk 5 tugas pertama. dan tugas lainnya akan ditambahkan ke antrian sampai antrian penuh (50 tugas).
10 adalah maxPoolSize - JVM dapat membuat maksimal 10 thread. Berarti jika sudah ada 5 task / thread yang sedang berjalan dan antrian sudah penuh dengan 50 tugas tertunda dan jika satu lagi request / tugas baru sudah masuk antrian maka JVM akan membuat thread baru hingga 10 (total threads = sebelumnya 5 + baru 5) ;
new ArrayBlockingQueue (50) = adalah ukuran antrian total - dapat mengantri 50 tugas di dalamnya.
setelah semua 10 utas berjalan dan jika tugas baru tiba, maka tugas baru itu akan ditolak.
Aturan untuk membuat Thread secara internal oleh SUN:
Jika jumlah utas kurang dari corePoolSize, buat utas baru untuk menjalankan tugas baru.
Jika jumlah utas sama (atau lebih besar dari) corePoolSize, letakkan tugas ke dalam antrian.
Jika antrean penuh, dan jumlah utas kurang dari maxPoolSize, buat utas baru untuk menjalankan tugas.
Jika antrian penuh, dan jumlah utas lebih besar dari atau sama dengan maxPoolSize, tolak tugas.
Harapan, Ini HelpFul .. dan tolong perbaiki saya jika saya salah ...
Dari dokumen :
Saat tugas baru dikirimkan dalam metode eksekusi (java.lang.Runnable), dan lebih sedikit dari thread corePoolSize yang berjalan, thread baru akan dibuat untuk menangani permintaan tersebut, bahkan jika thread pekerja lain tidak aktif. Jika ada lebih dari corePoolSize tetapi kurang dari maximumPoolSize threads yang berjalan, thread baru akan dibuat hanya jika antrian penuh.
Selanjutnya:
Dengan menyetel corePoolSize dan maximumPoolSize yang sama, Anda membuat kumpulan utas ukuran tetap. Dengan menyetel maximumPoolSize ke nilai yang pada dasarnya tidak dibatasi seperti Integer.MAX_VALUE, Anda mengizinkan kumpulan untuk mengakomodasi sejumlah tugas serentak yang berubah-ubah. Biasanya, ukuran inti dan kumpulan maksimum disetel hanya pada konstruksi, tetapi mereka juga dapat diubah secara dinamis menggunakan setCorePoolSize (int) dan setMaximumPoolSize (int).
Jika Anda memutuskan untuk membuat ThreadPoolExecutor
secara manual daripada menggunakan Executors
kelas pabrik, Anda perlu membuat dan mengonfigurasinya menggunakan salah satu konstruktornya. Konstruktor paling luas dari kelas ini adalah:
public ThreadPoolExecutor(
int corePoolSize,
int maxPoolSize,
long keepAlive,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler
);
Seperti yang Anda lihat, Anda dapat mengonfigurasi:
Membatasi jumlah tugas serentak yang sedang dijalankan, menentukan ukuran kumpulan thread Anda, mewakili manfaat besar bagi aplikasi Anda dan lingkungan eksekusinya dalam hal prediktabilitas dan stabilitas: pembuatan thread tak terbatas pada akhirnya akan menghabiskan sumber daya waktu proses dan aplikasi Anda mungkin mengalami sebagai konsekuensinya , masalah kinerja serius yang bahkan dapat menyebabkan ketidakstabilan aplikasi.
Itu adalah solusi hanya untuk satu bagian dari masalah: Anda membatasi jumlah tugas yang sedang dijalankan tetapi tidak membatasi jumlah pekerjaan yang dapat dikirim dan diantrekan untuk dieksekusi nanti. Aplikasi akan mengalami kekurangan sumber daya nanti, tetapi pada akhirnya akan mengalaminya jika tingkat pengiriman secara konsisten melebihi tingkat eksekusi.
Solusi untuk masalah ini adalah: Menyediakan antrian pemblokiran kepada pelaksana untuk menahan tugas yang menunggu. Jika antrian terisi, tugas yang dikirimkan akan "ditolak". The RejectedExecutionHandler
dipanggil saat pengajuan tugas ditolak, dan bahwa itu mengapa kata kerja ditolak dikutip dalam item sebelumnya. Anda dapat menerapkan kebijakan penolakan Anda sendiri atau menggunakan salah satu kebijakan bawaan yang disediakan oleh framework.
Kebijakan penolakan default membuat pelaksana melempar RejectedExecutionException
. Namun, kebijakan bawaan lainnya memungkinkan Anda:
Aturan ukuran kumpulan ThreadPoolExecutor
Aturan untuk ukuran ThreadPoolExecutor's
kolam umumnya salah dipahami, karena tidak berfungsi sebagaimana yang Anda pikirkan atau cara yang Anda inginkan.
Ambil contoh ini. Ukuran kumpulan utas awal adalah 1, ukuran kumpulan inti adalah 5, ukuran kumpulan maksimum adalah 10 dan antrian adalah 100.
Cara Sun: karena permintaan masuk utas akan dibuat hingga 5, maka tugas akan ditambahkan ke antrian sampai mencapai 100. Ketika antrian penuh, utas baru akan dibuat hingga maxPoolSize
. Setelah semua utas digunakan dan antrian selesai, tugas akan ditolak. Saat antrian berkurang, begitu pula jumlah utas aktif.
Cara yang diantisipasi pengguna: karena permintaan yang masuk dalam utas akan dibuat hingga 10, kemudian tugas akan ditambahkan ke antrean hingga mencapai 100 di mana mereka ditolak. Jumlah utas akan berganti nama maksimal sampai antrian kosong. Ketika antrian kosong, utas akan mati sampai corePoolSize
tersisa.
Perbedaannya adalah bahwa pengguna ingin mulai meningkatkan ukuran kolam lebih awal dan menginginkan antrian yang lebih kecil, sedangkan metode Sun ingin menjaga ukuran kolam tetap kecil dan hanya meningkatkannya setelah beban menjadi banyak.
Berikut adalah aturan Sun untuk pembuatan utas dalam istilah sederhana:
corePoolSize
, buat utas baru untuk menjalankan tugas baru.corePoolSize
, letakkan tugas ke antrian.maxPoolSize
, buat utas baru untuk menjalankan tugas.maxPoolSize
, tolak tugas. Panjang dan pendeknya adalah utas baru hanya dibuat ketika antrian terisi, jadi jika Anda menggunakan antrian tak terbatas maka jumlah utas tidak akan melebihi corePoolSize
.Untuk penjelasan yang lebih lengkap, dapatkan dari mulut kuda: ThreadPoolExecutor
dokumentasi API.
Ada posting forum yang sangat bagus yang memandu Anda tentang cara ThreadPoolExecutor
kerjanya dengan contoh kode: http://forums.sun.com/thread.jspa?threadID=5401400&tstart=0
Info lebih lanjut: http://forums.sun.com/thread.jspa?threadID=5224557&tstart=450
Anda dapat menemukan definisi dari istilah corepoolsize dan maxpoolsize di javadoc. http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html
Tautan di atas memiliki jawaban atas pertanyaan Anda. Namun, hanya untuk memperjelas. Aplikasi akan terus membuat utas hingga mencapai corePoolSize. Saya pikir idenya di sini adalah bahwa banyak utas ini harus cukup untuk menangani masuknya tugas. Jika tugas baru muncul setelah utas corePoolSize dibuat, tugas akan diantrekan. Setelah antrian penuh, pelaksana akan mulai membuat utas baru. Ini semacam keseimbangan. Apa yang dimaksud pada dasarnya adalah bahwa arus masuk tugas lebih dari sekadar kapasitas pemrosesan. Jadi, Pelaksana akan mulai membuat utas baru lagi hingga mencapai jumlah utas Maks. Sekali lagi, utas baru akan dibuat jika dan hanya jika antrian penuh.
Penjelasan bagus di blog ini :
public class ThreadPoolExecutorExample {
public static void main (String[] args) {
createAndRunPoolForQueue(new ArrayBlockingQueue<Runnable>(3), "Bounded");
createAndRunPoolForQueue(new LinkedBlockingDeque<>(), "Unbounded");
createAndRunPoolForQueue(new SynchronousQueue<Runnable>(), "Direct hand-off");
}
private static void createAndRunPoolForQueue (BlockingQueue<Runnable> queue,
String msg) {
System.out.println("---- " + msg + " queue instance = " +
queue.getClass()+ " -------------");
ThreadPoolExecutor e = new ThreadPoolExecutor(2, 5, Long.MAX_VALUE,
TimeUnit.NANOSECONDS, queue);
for (int i = 0; i < 10; i++) {
try {
e.execute(new Task());
} catch (RejectedExecutionException ex) {
System.out.println("Task rejected = " + (i + 1));
}
printStatus(i + 1, e);
}
e.shutdownNow();
System.out.println("--------------------\n");
}
private static void printStatus (int taskSubmitted, ThreadPoolExecutor e) {
StringBuilder s = new StringBuilder();
s.append("poolSize = ")
.append(e.getPoolSize())
.append(", corePoolSize = ")
.append(e.getCorePoolSize())
.append(", queueSize = ")
.append(e.getQueue()
.size())
.append(", queueRemainingCapacity = ")
.append(e.getQueue()
.remainingCapacity())
.append(", maximumPoolSize = ")
.append(e.getMaximumPoolSize())
.append(", totalTasksSubmitted = ")
.append(taskSubmitted);
System.out.println(s.toString());
}
private static class Task implements Runnable {
@Override
public void run () {
while (true) {
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
break;
}
}
}
}
}
Keluaran:
---- Bounded queue instance = class java.util.concurrent.ArrayBlockingQueue -------------
poolSize = 1, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 3, maximumPoolSize = 5, totalTasksSubmitted = 1
poolSize = 2, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 3, maximumPoolSize = 5, totalTasksSubmitted = 2
poolSize = 2, corePoolSize = 2, queueSize = 1, queueRemainingCapacity = 2, maximumPoolSize = 5, totalTasksSubmitted = 3
poolSize = 2, corePoolSize = 2, queueSize = 2, queueCapacity = 1, maximumPoolSize = 5, totalTasksSubmitted = 4
poolSize = 2, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 5
poolSize = 3, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 6
poolSize = 4, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 7
poolSize = 5, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 8
Task rejected = 9
poolSize = 5, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 9
Task rejected = 10
poolSize = 5, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 10
--------------------
---- Unbounded queue instance = class java.util.concurrent.LinkedBlockingDeque -------------
poolSize = 1, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 2147483647, maximumPoolSize = 5, totalTasksSubmitted = 1
poolSize = 2, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 2147483647, maximumPoolSize = 5, totalTasksSubmitted = 2
poolSize = 2, corePoolSize = 2, queueSize = 1, queueRemainingCapacity = 2147483646, maximumPoolSize = 5, totalTasksSubmitted = 3
poolSize = 2, corePoolSize = 2, queueSize = 2, queueRemainingCapacity = 2147483645, maximumPoolSize = 5, totalTasksSubmitted = 4
poolSize = 2, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 2147483644, maximumPoolSize = 5, totalTasksSubmitted = 5
poolSize = 2, corePoolSize = 2, queueSize = 4, queueRemainingCapacity = 2147483643, maximumPoolSize = 5, totalTasksSubmitted = 6
poolSize = 2, corePoolSize = 2, queueSize = 5, queueRemainingCapacity = 2147483642, maximumPoolSize = 5, totalTasksSubmitted = 7
poolSize = 2, corePoolSize = 2, queueSize = 6, queueRemainingCapacity = 2147483641, maximumPoolSize = 5, totalTasksSubmitted = 8
poolSize = 2, corePoolSize = 2, queueSize = 7, queueRemainingCapacity = 2147483640, maximumPoolSize = 5, totalTasksSubmitted = 9
poolSize = 2, corePoolSize = 2, queueSize = 8, queueRemainingCapacity = 2147483639, maximumPoolSize = 5, totalTasksSubmitted = 10
--------------------
---- Direct hand-off queue instance = class java.util.concurrent.SynchronousQueue -------------
poolSize = 1, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 1
poolSize = 2, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 2
poolSize = 3, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 3
poolSize = 4, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 4
poolSize = 5, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 5
Task rejected = 6
poolSize = 5, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 6
Task rejected = 7
poolSize = 5, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 7
Task rejected = 8
poolSize = 5, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 8
Task rejected = 9
poolSize = 5, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 9
Task rejected = 10
poolSize = 5, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 10
--------------------
Process finished with exit code 0
Dari buku esensi konkurensi Java :
CorePoolSize : ThreadPoolExecutor memiliki atribut corePoolSize yang menentukan berapa banyak utas yang akan dimulai hingga utas baru hanya dimulai ketika antrian penuh
MaximumPoolSize : Atribut ini menentukan berapa banyak utas yang dimulai secara maksimal. Anda dapat mengatur ini ke Integer. MAX_VALUE agar tidak memiliki batas atas
java.util.concurrent.ThreadPoolExecutor
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
Memahami perilaku internal ThreadPoolExecutor
ketika tugas baru dikirimkan membantu saya memahami bagaimana corePoolSize
dan maximumPoolSize
berbeda.
Membiarkan:
N
menjadi jumlah utas di pangkalan getPoolSize()
,. Utas aktif + utas diam.T
menjadi jumlah tugas yang diserahkan ke pelaksana / kumpulan.C
menjadi ukuran kumpulan inti , getCorePoolSize()
. Berapa banyak utas yang dapat dibuat paling banyak per kumpulan untuk tugas masuk sebelum tugas baru masuk ke antrean .M
menjadi ukuran kolam maksimum , getMaximumPoolSize()
. Jumlah maksimum utas yang dapat dialokasikan kumpulan.Perilaku ThreadPoolExecutor
di Java saat tugas baru dikirimkan:
N <= C
, utas diam tidak diberi tugas masuk baru, melainkan utas baru dibuat.N > C
dan jika ada utas yang tidak aktif, maka tugas baru ditetapkan di sana.N > C
dan jika TIDAK ada utas yang tidak aktif, tugas baru dimasukkan ke dalam antrian. TIDAK ADA BENANG BARU YANG DIBUAT DI SINI.M
. Jika M
tercapai, kami menolak tugas. Yang penting untuk tidak dilakukan di sini adalah kami tidak membuat utas baru hingga antrian penuh!Sumber:
corePoolSize = 0
dan maximumPoolSize = 10
dengan kapasitas antrian 50
.Ini akan menghasilkan satu utas aktif di kumpulan hingga antrean memiliki 50 item di dalamnya.
executor.execute(task #1):
before task #1 submitted to executor: java.util.concurrent.ThreadPoolExecutor@c52dafe[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
after task #1 submitted to executor: java.util.concurrent.ThreadPoolExecutor@c52dafe[Running, pool size = 1, active threads = 1, queued tasks = 1, completed tasks = 0]
[task #1 immediately queued and kicked in b/c the very first thread is created when `workerCountOf(recheck) == 0`]
execute(task #2):
before task #2 submitted to executor: java.util.concurrent.ThreadPoolExecutor@c52dafe[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
after task #2 submitted to executor: java.util.concurrent.ThreadPoolExecutor@c52dafe[Running, pool size = 1, active threads = 1, queued tasks = 1, completed tasks = 0]
[task #2 not starting before #1 is done]
... executed a few tasks...
execute(task #19)
before task #19 submitted to executor: java.util.concurrent.ThreadPoolExecutor@735afe38[Running, pool size = 1, active threads = 1, queued tasks = 17, completed tasks = 0]
after task #19 submitted to executor: java.util.concurrent.ThreadPoolExecutor@735afe38[Running, pool size = 1, active threads = 1, queued tasks = 18, completed tasks = 0]
...
execute(task #51)
before task submitted to executor: java.util.concurrent.ThreadPoolExecutor@735afe38[Running, pool size = 1, active threads = 1, queued tasks = 50, completed tasks = 0]
after task submitted to executor: java.util.concurrent.ThreadPoolExecutor@735afe38[Running, pool size = 2, active threads = 2, queued tasks = 50, completed tasks = 0]
Queue is full.
A new thread was created as the queue was full.
corePoolSize = 10
dan maximumPoolSize = 10
dengan kapasitas antrian 50
.Ini akan menghasilkan 10 utas aktif di kumpulan. Ketika antrian memiliki 50 item di dalamnya, tugas akan ditolak.
execute(task #1)
before task #1 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
after task #1 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
execute(task #2)
before task #2 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
after task #2 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 0]
execute(task #3)
before task #3 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 0]
after task #3 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 3, active threads = 3, queued tasks = 0, completed tasks = 0]
... executed a few tasks...
execute(task #11)
before task #11 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 10, active threads = 10, queued tasks = 0, completed tasks = 0]
after task #11 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 10, active threads = 10, queued tasks = 1, completed tasks = 0]
... executed a few tasks...
execute(task #51)
before task #51 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 10, active threads = 10, queued tasks = 50, completed tasks = 0]
Task was rejected as we have reached `maximumPoolSize`.