logo

Пул потоків Java

Пул потоків Java представляє групу робочих потоків, які очікують виконання завдання та багато разів повторно використовуються.

У випадку пулу потоків створюється група потоків фіксованого розміру. Потік із пулу потоків витягується та призначається завдання постачальником послуг. Після завершення завдання потік знову міститься в пулі потоків.

Методи пулу потоків

newFixedThreadPool(int s): Метод створює пул потоків фіксованого розміру s.

newCachedThreadPool(): Метод створює новий пул потоків, який створює нові потоки, коли це необхідно, але все одно використовуватиме раніше створений потік, коли вони доступні для використання.

newSingleThreadExecutor(): Метод створює новий потік.

Перевага пулу потоків Java

Краща продуктивність Це економить час, оскільки немає необхідності створювати новий потік.

Використання в реальному часі

Він використовується в Servlet і JSP, де контейнер створює пул потоків для обробки запиту.

Приклад пулу потоків Java

Давайте розглянемо простий приклад пулу потоків Java з використанням ExecutorService і Executors.

Файл: WorkerThread.java

 import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; class WorkerThread implements Runnable { private String message; public WorkerThread(String s){ this.message=s; } public void run() { System.out.println(Thread.currentThread().getName()+' (Start) message = '+message); processmessage();//call processmessage method that sleeps the thread for 2 seconds System.out.println(Thread.currentThread().getName()+' (End)');//prints thread name } private void processmessage() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } 

Файл: TestThreadPool.java

 public class TestThreadPool { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(5);//creating a pool of 5 threads for (int i = 0; i <10; i++) { runnable worker="new" workerthread('' + i); executor.execute(worker); calling execute method of executorservice } executor.shutdown(); while (!executor.isterminated()) system.out.println('finished all threads'); < pre> <p> <strong>Output:</strong> </p> <pre>pool-1-thread-1 (Start) message = 0 pool-1-thread-2 (Start) message = 1 pool-1-thread-3 (Start) message = 2 pool-1-thread-5 (Start) message = 4 pool-1-thread-4 (Start) message = 3 pool-1-thread-2 (End) pool-1-thread-2 (Start) message = 5 pool-1-thread-1 (End) pool-1-thread-1 (Start) message = 6 pool-1-thread-3 (End) pool-1-thread-3 (Start) message = 7 pool-1-thread-4 (End) pool-1-thread-4 (Start) message = 8 pool-1-thread-5 (End) pool-1-thread-5 (Start) message = 9 pool-1-thread-2 (End) pool-1-thread-1 (End) pool-1-thread-4 (End) pool-1-thread-3 (End) pool-1-thread-5 (End) Finished all threads </pre> download this example <h2>Thread Pool Example: 2</h2> <p>Let&apos;s see another example of the thread pool.</p> <p> <strong>FileName:</strong> ThreadPoolExample.java</p> <pre> // important import statements import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.text.SimpleDateFormat; class Tasks implements Runnable { private String taskName; // constructor of the class Tasks public Tasks(String str) { // initializing the field taskName taskName = str; } // Printing the task name and then sleeps for 1 sec // The complete process is getting repeated five times public void run() { try { for (int j = 0; j <= 5; j++) { if (j="=" 0) date dt="new" date(); simpledateformat sdf="new" simpledateformat('hh : mm ss'); prints the initialization time for every task system.out.println('initialization name: '+ taskname + '=" + sdf.format(dt)); } else { Date dt = new Date(); SimpleDateFormat sdf = new SimpleDateFormat(" hh execution system.out.println('time of is complete.'); } catch(interruptedexception ie) ie.printstacktrace(); public class threadpoolexample maximum number threads in thread pool static final int max_th="3;" main method void main(string argvs[]) creating five new tasks runnable rb1="new" tasks('task 1'); rb2="new" 2'); rb3="new" 3'); rb4="new" 4'); rb5="new" 5'); a with size fixed executorservice pl="Executors.newFixedThreadPool(MAX_TH);" passes objects to execute (step 3) pl.execute(rb1); pl.execute(rb2); pl.execute(rb3); pl.execute(rb4); pl.execute(rb5); shutdown pl.shutdown(); < pre> <p> <strong>Output:</strong> </p> <pre> Initialization time for the task name: task 1 = 06 : 13 : 02 Initialization time for the task name: task 2 = 06 : 13 : 02 Initialization time for the task name: task 3 = 06 : 13 : 02 Time of execution for the task name: task 1 = 06 : 13 : 04 Time of execution for the task name: task 2 = 06 : 13 : 04 Time of execution for the task name: task 3 = 06 : 13 : 04 Time of execution for the task name: task 1 = 06 : 13 : 05 Time of execution for the task name: task 2 = 06 : 13 : 05 Time of execution for the task name: task 3 = 06 : 13 : 05 Time of execution for the task name: task 1 = 06 : 13 : 06 Time of execution for the task name: task 2 = 06 : 13 : 06 Time of execution for the task name: task 3 = 06 : 13 : 06 Time of execution for the task name: task 1 = 06 : 13 : 07 Time of execution for the task name: task 2 = 06 : 13 : 07 Time of execution for the task name: task 3 = 06 : 13 : 07 Time of execution for the task name: task 1 = 06 : 13 : 08 Time of execution for the task name: task 2 = 06 : 13 : 08 Time of execution for the task name: task 3 = 06 : 13 : 08 task 2 is complete. Initialization time for the task name: task 4 = 06 : 13 : 09 task 1 is complete. Initialization time for the task name: task 5 = 06 : 13 : 09 task 3 is complete. Time of execution for the task name: task 4 = 06 : 13 : 10 Time of execution for the task name: task 5 = 06 : 13 : 10 Time of execution for the task name: task 4 = 06 : 13 : 11 Time of execution for the task name: task 5 = 06 : 13 : 11 Time of execution for the task name: task 4 = 06 : 13 : 12 Time of execution for the task name: task 5 = 06 : 13 : 12 Time of execution for the task name: task 4 = 06 : 13 : 13 Time of execution for the task name: task 5 = 06 : 13 : 13 Time of execution for the task name: task 4 = 06 : 13 : 14 Time of execution for the task name: task 5 = 06 : 13 : 14 task 4 is complete. task 5 is complete. </pre> <p> <strong>Explanation:</strong> It is evident by looking at the output of the program that tasks 4 and 5 are executed only when the thread has an idle thread. Until then, the extra tasks are put in the queue.</p> <p>The takeaway from the above example is when one wants to execute 50 tasks but is not willing to create 50 threads. In such a case, one can create a pool of 10 threads. Thus, 10 out of 50 tasks are assigned, and the rest are put in the queue. Whenever any thread out of 10 threads becomes idle, it picks up the 11<sup>th </sup>task. The other pending tasks are treated the same way.</p> <h2>Risks involved in Thread Pools</h2> <p>The following are the risk involved in the thread pools.</p> <p> <strong>Deadlock:</strong> It is a known fact that deadlock can come in any program that involves multithreading, and a thread pool introduces another scenario of deadlock. Consider a scenario where all the threads that are executing are waiting for the results from the threads that are blocked and waiting in the queue because of the non-availability of threads for the execution.</p> <p> <strong>Thread Leakage:</strong> Leakage of threads occurs when a thread is being removed from the pool to execute a task but is not returning to it after the completion of the task. For example, when a thread throws the exception and the pool class is not able to catch this exception, then the thread exits and reduces the thread pool size by 1. If the same thing repeats a number of times, then there are fair chances that the pool will become empty, and hence, there are no threads available in the pool for executing other requests.</p> <p> <strong>Resource Thrashing:</strong> A lot of time is wasted in context switching among threads when the size of the thread pool is very large. Whenever there are more threads than the optimal number may cause the starvation problem, and it leads to resource thrashing.</p> <h2>Points to Remember</h2> <p>Do not queue the tasks that are concurrently waiting for the results obtained from the other tasks. It may lead to a deadlock situation, as explained above.</p> <p>Care must be taken whenever threads are used for the operation that is long-lived. It may result in the waiting of thread forever and will finally lead to the leakage of the resource.</p> <p>In the end, the thread pool has to be ended explicitly. If it does not happen, then the program continues to execute, and it never ends. Invoke the shutdown() method on the thread pool to terminate the executor. Note that if someone tries to send another task to the executor after shutdown, it will throw a RejectedExecutionException.</p> <p>One needs to understand the tasks to effectively tune the thread pool. If the given tasks are contrasting, then one should look for pools for executing different varieties of tasks so that one can properly tune them.</p> <p>To reduce the probability of running JVM out of memory, one can control the maximum threads that can run in JVM. The thread pool cannot create new threads after it has reached the maximum limit.</p> <p>A thread pool can use the same used thread if the thread has finished its execution. Thus, the time and resources used for the creation of a new thread are saved.</p> <h2>Tuning the Thread Pool</h2> <p>The accurate size of a thread pool is decided by the number of available processors and the type of tasks the threads have to execute. If a system has the P processors that have only got the computation type processes, then the maximum size of the thread pool of P or P + 1 achieves the maximum efficiency. However, the tasks may have to wait for I/O, and in such a scenario, one has to take into consideration the ratio of the waiting time (W) and the service time (S) for the request; resulting in the maximum size of the pool P * (1 + W / S) for the maximum efficiency.</p> <h2>Conclusion</h2> <p>A thread pool is a very handy tool for organizing applications, especially on the server-side. Concept-wise, a thread pool is very easy to comprehend. However, one may have to look at a lot of issues when dealing with a thread pool. It is because the thread pool comes with some risks involved it (risks are discussed above).</p> <hr></=></pre></10;>
завантажити цей приклад

Приклад пулу потоків: 2

Давайте подивимося інший приклад пулу потоків.

Ім'я файлу: ThreadPoolExample.java

 // important import statements import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.text.SimpleDateFormat; class Tasks implements Runnable { private String taskName; // constructor of the class Tasks public Tasks(String str) { // initializing the field taskName taskName = str; } // Printing the task name and then sleeps for 1 sec // The complete process is getting repeated five times public void run() { try { for (int j = 0; j <= 5; j++) { if (j="=" 0) date dt="new" date(); simpledateformat sdf="new" simpledateformat(\'hh : mm ss\'); prints the initialization time for every task system.out.println(\'initialization name: \'+ taskname + \'=" + sdf.format(dt)); } else { Date dt = new Date(); SimpleDateFormat sdf = new SimpleDateFormat(" hh execution system.out.println(\'time of is complete.\'); } catch(interruptedexception ie) ie.printstacktrace(); public class threadpoolexample maximum number threads in thread pool static final int max_th="3;" main method void main(string argvs[]) creating five new tasks runnable rb1="new" tasks(\'task 1\'); rb2="new" 2\'); rb3="new" 3\'); rb4="new" 4\'); rb5="new" 5\'); a with size fixed executorservice pl="Executors.newFixedThreadPool(MAX_TH);" passes objects to execute (step 3) pl.execute(rb1); pl.execute(rb2); pl.execute(rb3); pl.execute(rb4); pl.execute(rb5); shutdown pl.shutdown(); < pre> <p> <strong>Output:</strong> </p> <pre> Initialization time for the task name: task 1 = 06 : 13 : 02 Initialization time for the task name: task 2 = 06 : 13 : 02 Initialization time for the task name: task 3 = 06 : 13 : 02 Time of execution for the task name: task 1 = 06 : 13 : 04 Time of execution for the task name: task 2 = 06 : 13 : 04 Time of execution for the task name: task 3 = 06 : 13 : 04 Time of execution for the task name: task 1 = 06 : 13 : 05 Time of execution for the task name: task 2 = 06 : 13 : 05 Time of execution for the task name: task 3 = 06 : 13 : 05 Time of execution for the task name: task 1 = 06 : 13 : 06 Time of execution for the task name: task 2 = 06 : 13 : 06 Time of execution for the task name: task 3 = 06 : 13 : 06 Time of execution for the task name: task 1 = 06 : 13 : 07 Time of execution for the task name: task 2 = 06 : 13 : 07 Time of execution for the task name: task 3 = 06 : 13 : 07 Time of execution for the task name: task 1 = 06 : 13 : 08 Time of execution for the task name: task 2 = 06 : 13 : 08 Time of execution for the task name: task 3 = 06 : 13 : 08 task 2 is complete. Initialization time for the task name: task 4 = 06 : 13 : 09 task 1 is complete. Initialization time for the task name: task 5 = 06 : 13 : 09 task 3 is complete. Time of execution for the task name: task 4 = 06 : 13 : 10 Time of execution for the task name: task 5 = 06 : 13 : 10 Time of execution for the task name: task 4 = 06 : 13 : 11 Time of execution for the task name: task 5 = 06 : 13 : 11 Time of execution for the task name: task 4 = 06 : 13 : 12 Time of execution for the task name: task 5 = 06 : 13 : 12 Time of execution for the task name: task 4 = 06 : 13 : 13 Time of execution for the task name: task 5 = 06 : 13 : 13 Time of execution for the task name: task 4 = 06 : 13 : 14 Time of execution for the task name: task 5 = 06 : 13 : 14 task 4 is complete. task 5 is complete. </pre> <p> <strong>Explanation:</strong> It is evident by looking at the output of the program that tasks 4 and 5 are executed only when the thread has an idle thread. Until then, the extra tasks are put in the queue.</p> <p>The takeaway from the above example is when one wants to execute 50 tasks but is not willing to create 50 threads. In such a case, one can create a pool of 10 threads. Thus, 10 out of 50 tasks are assigned, and the rest are put in the queue. Whenever any thread out of 10 threads becomes idle, it picks up the 11<sup>th </sup>task. The other pending tasks are treated the same way.</p> <h2>Risks involved in Thread Pools</h2> <p>The following are the risk involved in the thread pools.</p> <p> <strong>Deadlock:</strong> It is a known fact that deadlock can come in any program that involves multithreading, and a thread pool introduces another scenario of deadlock. Consider a scenario where all the threads that are executing are waiting for the results from the threads that are blocked and waiting in the queue because of the non-availability of threads for the execution.</p> <p> <strong>Thread Leakage:</strong> Leakage of threads occurs when a thread is being removed from the pool to execute a task but is not returning to it after the completion of the task. For example, when a thread throws the exception and the pool class is not able to catch this exception, then the thread exits and reduces the thread pool size by 1. If the same thing repeats a number of times, then there are fair chances that the pool will become empty, and hence, there are no threads available in the pool for executing other requests.</p> <p> <strong>Resource Thrashing:</strong> A lot of time is wasted in context switching among threads when the size of the thread pool is very large. Whenever there are more threads than the optimal number may cause the starvation problem, and it leads to resource thrashing.</p> <h2>Points to Remember</h2> <p>Do not queue the tasks that are concurrently waiting for the results obtained from the other tasks. It may lead to a deadlock situation, as explained above.</p> <p>Care must be taken whenever threads are used for the operation that is long-lived. It may result in the waiting of thread forever and will finally lead to the leakage of the resource.</p> <p>In the end, the thread pool has to be ended explicitly. If it does not happen, then the program continues to execute, and it never ends. Invoke the shutdown() method on the thread pool to terminate the executor. Note that if someone tries to send another task to the executor after shutdown, it will throw a RejectedExecutionException.</p> <p>One needs to understand the tasks to effectively tune the thread pool. If the given tasks are contrasting, then one should look for pools for executing different varieties of tasks so that one can properly tune them.</p> <p>To reduce the probability of running JVM out of memory, one can control the maximum threads that can run in JVM. The thread pool cannot create new threads after it has reached the maximum limit.</p> <p>A thread pool can use the same used thread if the thread has finished its execution. Thus, the time and resources used for the creation of a new thread are saved.</p> <h2>Tuning the Thread Pool</h2> <p>The accurate size of a thread pool is decided by the number of available processors and the type of tasks the threads have to execute. If a system has the P processors that have only got the computation type processes, then the maximum size of the thread pool of P or P + 1 achieves the maximum efficiency. However, the tasks may have to wait for I/O, and in such a scenario, one has to take into consideration the ratio of the waiting time (W) and the service time (S) for the request; resulting in the maximum size of the pool P * (1 + W / S) for the maximum efficiency.</p> <h2>Conclusion</h2> <p>A thread pool is a very handy tool for organizing applications, especially on the server-side. Concept-wise, a thread pool is very easy to comprehend. However, one may have to look at a lot of issues when dealing with a thread pool. It is because the thread pool comes with some risks involved it (risks are discussed above).</p> <hr></=>

Пояснення: Переглядаючи вихідні дані програми, стає очевидним, що завдання 4 і 5 виконуються лише тоді, коли потік має неактивний потік. До цього часу додаткові завдання ставляться в чергу.

Висновок із наведеного вище прикладу: хтось хоче виконати 50 завдань, але не бажає створювати 50 потоків. У такому випадку можна створити пул із 10 потоків. Таким чином, з 50 завдань призначаються 10, а решта ставляться в чергу. Щоразу, коли будь-який з 10 потоків стає неактивним, він підхоплює 11тисзавдання. Інші незавершені завдання обробляються таким же чином.

Ризики, пов’язані з пулами потоків

Нижче наведено ризики, пов’язані з пулами потоків.

Тупик: Відомий факт, що взаємоблокування може виникнути в будь-якій програмі, яка передбачає багатопотоковість, а пул потоків представляє інший сценарій тупикової блокування. Розглянемо сценарій, коли всі потоки, які виконуються, очікують результатів від потоків, які заблоковані та очікують у черзі через відсутність потоків для виконання.

Витік нитки: Витік потоків відбувається, коли потік видаляється з пулу для виконання завдання, але не повертається до нього після завершення завдання. Наприклад, коли потік генерує виняток, а клас пулу не в змозі перехопити цей виняток, тоді потік завершує роботу та зменшує розмір пулу потоків на 1. Якщо одне і те ж повторюється кілька разів, то є хороші шанси, що пул стане порожнім, і, отже, в пулі не буде потоків, доступних для виконання інших запитів.

Розбиття ресурсів: Багато часу витрачається на перемикання контексту між потоками, коли розмір пулу потоків дуже великий. Щоразу, коли потоків більше, ніж оптимальна кількість, це може спричинити проблему голодування, що призводить до вичерпання ресурсів.

Пункти, які слід пам’ятати

Не ставте в чергу завдання, які одночасно очікують на результати, отримані від інших завдань. Це може призвести до тупикової ситуації, як пояснювалося вище.

Необхідно бути обережним, коли для операції використовуються нитки, які є довговічними. Це може призвести до вічного очікування потоку і, зрештою, призведе до витоку ресурсу.

Зрештою, пул потоків має бути закрито явно. Якщо цього не відбувається, то програма продовжує виконуватися і ніколи не закінчується. Викличте метод shutdown() у пулі потоків, щоб завершити роботу виконавця. Зауважте, що якщо хтось спробує надіслати інше завдання виконавцю після завершення роботи, це викличе виняткову ситуацію RejectedExecutionException.

Потрібно розуміти завдання, щоб ефективно налаштувати пул потоків. Якщо задані завдання контрастні, то слід шукати пули для виконання різних різновидів завдань, щоб правильно їх налаштувати.

Щоб зменшити ймовірність запуску JVM з пам’яті, можна контролювати максимальну кількість потоків, які можуть виконуватися в JVM. Пул потоків не може створювати нові потоки після досягнення максимального ліміту.

Пул потоків може використовувати той самий використаний потік, якщо потік завершив своє виконання. Таким чином, економляться час і ресурси, що використовуються для створення нового потоку.

Налаштування пулу потоків

Точний розмір пулу потоків визначається кількістю доступних процесорів і типом завдань, які мають виконувати потоки. Якщо система має процесори P, які мають лише процеси типу обчислень, тоді максимальний розмір пулу потоків P або P + 1 забезпечує максимальну ефективність. Однак завданням може знадобитися чекати вводу-виводу, і в такому сценарії потрібно брати до уваги співвідношення часу очікування (W) і часу обслуговування (S) для запиту; в результаті чого максимальний розмір пулу P * (1 + W / S) для максимальної ефективності.

Висновок

Пул потоків є дуже зручним інструментом для організації програм, особливо на стороні сервера. З точки зору концепції, пул потоків дуже простий для розуміння. Однак при роботі з пулом потоків, можливо, доведеться розглянути багато проблем. Це тому, що пул потоків пов’язаний з деякими ризиками (ризики обговорювалися вище).

java спробуйте зловити