Thread Pools

Thread Pools are useful when you need to limit the number of threads running in your application at the same time. There is a performance overhead associated with starting a new thread, and each thread is also allocated some memory for its stack etc.
當您需要限制在應用程序中同時運行的線程數時,線程池很有用。啓動新線程會帶來性能開銷,並且每個線程還爲分配棧空間等。

Instead of starting a new thread for every task to execute concurrently, the task can be passed to a thread pool. As soon as the pool has any idle threads the task is assigned to one of them and executed. Internally the tasks are inserted into a Blocking Queue which the threads in the pool are dequeuing from. When a new task is inserted into the queue one of the idle threads will dequeue it successfully and execute it. The rest of the idle threads in the pool will be blocked waiting to dequeue tasks.
不必爲要同時執行的每個任務啓動新線程,而是可以將任務傳遞給線程池。一旦池中有任何空閒線程,便將任務分配給其中一個並執行。在內部將任務插入到阻塞隊列中,池中的線程將從中離開。將新任務插入隊列後,空閒線程之一將成功使它出隊並執行它。池中的其餘空閒線程將被阻塞,以等待出隊任務。

Thread pools are often used in multi threaded servers. Each connection arriving at the server via the network is wrapped as a task and passed on to a thread pool. The threads in the thread pool will process the requests on the connections concurrently. A later trail will get into detail about implementing multithreaded servers in Java.
線程池通常在多線程服務器中使用。通過網絡到達服務器的每個連接都被包裝爲一個任務,並傳遞給線程池。線程池中的線程將同時處理連接上的請求。後面的線索將詳細介紹有關在Java中實現多線程服務器的信息。

Java 5 comes with built in thread pools in the java.util.concurrent package, so you don’t have to implement your own thread pool. You can read more about it in my text on the java.util.concurrent.ExecutorService. Still it can be useful to know a bit about the implementation of a thread pool anyways.
Java 5在java.util.concurrent包中帶有內置的線程池,因此您不必實現自己的線程池。您可以在java.util.concurrent.ExecutorService的文本中閱讀有關此內容的更多信息。無論如何,瞭解線程池的實現還是很有用的。

How thread pool works in java

A thread pool is a collection of pre-initialized threads. Generally, the size of the collection is fixed, but it is not mandatory. It facilitates the execution of N number of tasks using the same threads. If there are more tasks than threads, then tasks need to wait in a queue like structure (FIFO – First in first out).
線程池是預初始化線程的集合。 通常,集合的大小是固定的,但不是強制性的。 它有助於使用相同的線程執行N個任務。 如果任務多於線程,則任務需要在類似結構的隊列中等待(FIFO –先進先出)。

When any thread completes its execution, it can pickup a new task from the queue and execute it. When all tasks are completed the threads remain active and wait for more tasks in the thread pool.
當任何線程完成其執行時,它可以從隊列中提取新任務並執行它。 完成所有任務後,線程將保持活動狀態並等待線程池中的更多任務。
在這裏插入圖片描述

ThreadPoolExecutor

A thread pool reuses previously created threads to execute current tasks and offers a solution to the problem of thread cycle overhead and resource thrashing. Since the thread is already existing when the request arrives, the delay introduced by thread creation is eliminated, making the application more responsive.
線程池可重複使用先前創建的線程來執行當前任務,併爲線程週期開銷和資源顛簸問題提供瞭解決方案。 由於在請求到達時線程已經存在,因此消除了線程創建帶來的延遲,從而使應用程序具有更高的響應速度。

  • Java provides the Executor framework which is centered around the Executor interface, its sub-interface –ExecutorService and the class-ThreadPoolExecutor, which implements both of these interfaces. By using the executor, one only has to implement the Runnable objects and send them to the executor to execute.
    在這裏插入圖片描述
    線程實現Runnable接口,就可以用Executor執行。
    在這裏插入圖片描述

  • They allow you to take advantage of threading, but focus on the tasks that you want the thread to perform, instead of thread mechanics.
    它們使您可以利用線程的優勢,但專注於希望線程執行的任務,而不是線程機制。

  • To use thread pools, we first create a object of ExecutorService and pass a set of tasks to it. ThreadPoolExecutor class allows to set the core and maximum pool size.The runnables that are run by a particular thread are executed sequentially.
    要使用線程池,我們首先創建一個ExecutorService對象,並將一組任務傳遞給它。 ThreadPoolExecutor類允許設置核心(池中所保存的線程數,包括空閒線程)和最大池大小(池中允許的最大線程數)。由特定線程運行的可運行對象按順序執行。
    在這裏插入圖片描述

Steps to be followed

  1. Create a task(Runnable Object) to execute
  2. Create Executor Pool using Executors
  3. Pass tasks to Executor Pool
  4. Shutdown the Executor Pool

Thread Pool Example

// Java program to illustrate 
// ThreadPool 
import java.text.SimpleDateFormat; 
import java.util.Date; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 

// Task class to be executed (Step 1) 
class Task implements Runnable 
{ 
	private String name; 
	
	public Task(String s) 
	{ 
		name = s; 
	} 
	
	// Prints task name and sleeps for 1s 
	// This Whole process is repeated 5 times 
	public void run() 
	{ 
		try
		{ 
			for (int i = 0; i<=5; i++) 
			{ 
				if (i==0) 
				{ 
					Date d = new Date(); 
					SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss"); 
					System.out.println("Initialization Time for"
							+ " task name - "+ name +" = " +ft.format(d)); 
					//prints the initialization time for every task 
				} 
				else
				{ 
					Date d = new Date(); 
					SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss"); 
					System.out.println("Executing Time for task name - "+ 
							name +" = " +ft.format(d)); 
					// prints the execution time for every task 
				}
				//注意在sleep時並不釋放線程持有鎖 
				Thread.sleep(1000); 
			} 
			System.out.println(name+" complete"); 
		} 
		
		catch(InterruptedException e) 
		{ 
			e.printStackTrace(); 
		} 
	} 
} 
public class Test 
{ 
	// Maximum number of threads in thread pool 
	static final int MAX_T = 3;			 

	public static void main(String[] args) 
	{ 
		// creates five tasks 
		Runnable r1 = new Task("task 1"); 
		Runnable r2 = new Task("task 2"); 
		Runnable r3 = new Task("task 3"); 
		Runnable r4 = new Task("task 4"); 
		Runnable r5 = new Task("task 5");	 
		
		// creates a thread pool with MAX_T no. of 
		// threads as the fixed pool size(Step 2) 
		ExecutorService pool = Executors.newFixedThreadPool(MAX_T); 
		
		// passes the Task objects to the pool to execute (Step 3) 
		pool.execute(r1); 
		pool.execute(r2); 
		pool.execute(r3); 
		pool.execute(r4); 
		pool.execute(r5); 
		
		// pool shutdown ( Step 4) 
		pool.shutdown();	 
	} 
} 

Sample Execution

Output:
Initialization Time for task name - task 2 = 02:32:56
Initialization Time for task name - task 1 = 02:32:56
Initialization Time for task name - task 3 = 02:32:56
Executing Time for task name - task 1 = 02:32:57
Executing Time for task name - task 2 = 02:32:57
Executing Time for task name - task 3 = 02:32:57
Executing Time for task name - task 1 = 02:32:58
Executing Time for task name - task 2 = 02:32:58
Executing Time for task name - task 3 = 02:32:58
Executing Time for task name - task 1 = 02:32:59
Executing Time for task name - task 2 = 02:32:59
Executing Time for task name - task 3 = 02:32:59
Executing Time for task name - task 1 = 02:33:00
Executing Time for task name - task 3 = 02:33:00
Executing Time for task name - task 2 = 02:33:00
Executing Time for task name - task 2 = 02:33:01
Executing Time for task name - task 1 = 02:33:01
Executing Time for task name - task 3 = 02:33:01
task 2 complete
task 1 complete
task 3 complete
Initialization Time for task name - task 5 = 02:33:02
Initialization Time for task name - task 4 = 02:33:02
Executing Time for task name - task 4 = 02:33:03
Executing Time for task name - task 5 = 02:33:03
Executing Time for task name - task 5 = 02:33:04
Executing Time for task name - task 4 = 02:33:04
Executing Time for task name - task 4 = 02:33:05
Executing Time for task name - task 5 = 02:33:05
Executing Time for task name - task 5 = 02:33:06
Executing Time for task name - task 4 = 02:33:06
Executing Time for task name - task 5 = 02:33:07
Executing Time for task name - task 4 = 02:33:07
task 5 complete
task 4 complete

As seen in the execution of the program, the task 4 or task 5 are executed only when a thread in the pool becomes idle. Until then, the extra tasks are placed in a queue.
從程序執行中可以看出,僅當池中的線程變爲空閒時才執行任務4或任務5。 在此之前,將多餘的任務放在隊列中。
在這裏插入圖片描述
在這裏插入圖片描述

Custom thread pool implementation in java

package com.howtodoinjava.threads;
 
import java.util.concurrent.LinkedBlockingQueue;
 
@SuppressWarnings("unused")
public class CustomThreadPool 
{
    //Thread pool size
    private final int poolSize;
     
    //Internally pool is an array
    private final WorkerThread[] workers;
     
    // FIFO ordering
    private final LinkedBlockingQueue<Runnable> queue;
 //在初始化時,啓動線程池工作線程
    public CustomThreadPool(int poolSize) 
    {
        this.poolSize = poolSize;
        queue = new LinkedBlockingQueue<Runnable>();
        workers = new WorkerThread[poolSize];
 
        for (int i = 0; i < poolSize; i++) {
            workers[i] = new WorkerThread();
            workers[i].start();
        }
    }
 
    public void execute(Runnable task) {
        synchronized (queue) {
            queue.add(task); //將任務放到任務隊列中
            queue.notify();
            /*
            初始時,工作線程已啓動,但是queue.isEmpty()=true,所以只能wait()
            現在queue加入了任務,則喚醒時不確定喚醒的是哪個工作線程
            用這些預定義的工作線程模擬線程池。
            */
        }
    }
 
    private class WorkerThread extends Thread {
        public void run() {
            Runnable task;
  /*
  注意這裏用了while(true),也就是工作線程隨時準備工作 只要有queue.add(task)
  則被喚醒繼續執行。
  synchronized(queue):雖然queue是線程安全的,但queue.wait必須用synchronized包裹
  */
            while (true) {
                synchronized (queue) {
                    while (queue.isEmpty()) {
                        try {
                            queue.wait();
                        } catch (InterruptedException e) {
                            System.out.println("An error occurred while queue is waiting: " + e.getMessage());
                        }
                    }
          //將任務從隊列取出,並在這個工作線程中執行
                    task = (Runnable) queue.poll();
                }
 /*
 任務執行並沒有放在同步塊裏,因爲元素取出是線程安全的,這個元素不會再被其他線程操作,
 所以task.run是線程安全的
 */             try {
                    task.run(); //這個run是Task實例中的run,並不是WorkerThread的run。
                } catch (RuntimeException e) {
                    System.out.println("Thread pool is interrupted due to an issue: " + e.getMessage());
                }
            }
        }
    }
 
    public void shutdown() {
        System.out.println("Shutting down thread pool");
        for (int i = 0; i < poolSize; i++) {
            workers[i] = null;
        }
    }
}

class Task implements Runnable {
    private String name;
 
    public Task(String name) {
        this.name = name;
    }
 
    public String getName() {
        return name;
    }
 
    public void run() {
        System.out.println("Executing : " + name + ", Current Seconds : " + new Date().getSeconds());
    }
package com.howtodoinjava.threads;
 
public class CustomThreadPoolExample 
{
    public static void main(String[] args) 
    {
        CustomThreadPool customThreadPool = new CustomThreadPool(2);
         
        for (int i = 1; i <= 5; i++) 
        {
            Task task = new Task("Task " + i);
            System.out.println("Created : " + task.getName());
 
            customThreadPool.execute(task);
        }
    }
}
執行結果:
Created : Task 1
Created : Task 2
Created : Task 3
Created : Task 4
Created : Task 5
Executing : Task 1
Executing : Task 2
Executing : Task 3
Executing : Task 4
Executing : Task 5

參考:
https://www.geeksforgeeks.org/thread-pools-java/
https://howtodoinjava.com/java/multi-threading/java-thread-pool-executor-example/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章