Java併發之Executor

chi633發表於2017-12-21
  • 引入Executor
  • 建立Executor
  • 建立固定大小的執行緒Executor

引入Executor

我們在開發Java多執行緒程式的時候,往往會建立很多個Runnable物件,然後建立對應的Thread物件來執行它們。但是,如果需要開發一個大量的併發任務,過多的任務就會導致下面這些問題:

  • 必須給每個Runnable物件建立一個Thread,也就意味著要建立相關的執行緒建立,結束,取結果的程式碼,程式碼很冗餘
  • 過多的Thread物件對於降低了應用程式的效率,系統負荷過重

從Java 5之後,引入了一套API框架用來解決這個問題。這套新的框架就是執行器框架(Executor Framework),圍繞著Executor介面和它的自介面的ExecutorService,以及實現這兩個介面的ThreadPoolExecutor類。

部分繼承關係:

image.png

這套框架分離了任務的建立和執行。使用Executor,只要將Runnable物件,直接丟給執行器就可以了。Executor會自己建立執行緒,來負責這些Runnable物件任務的執行。Executor有一個好處就是利用執行緒池提高效能,當收到一個新任務時,會嘗試使用執行緒池中的空閒執行緒來執行,避免了重複建立過多的執行緒而導致系統效能的下降。

建立Executor

使用Executor的第一步就是建立一個執行緒池物件,java提供了Executors的工廠類,可以幫我們建立不同的執行緒池物件

image.png

然後呼叫Executor的execute方法執行相應的執行緒,並且要顯示的結束執行緒池 server類:

package CreateExecutor;

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class Server {
	private ThreadPoolExecutor executor;

	public Server() {
		executor = (ThreadPoolExecutor)Executors.newCachedThreadPool();
	}
	
	public void executorTask(Task task) {
		System.out.println("a new task arrived");
		executor.execute(task);
		System.out.println("Server : Pool Size :" + executor.getPoolSize());
		System.out.println("Server : ActiveCount :" + executor.getActiveCount());
		System.out.println("Server : CompletedTaskCount :" + executor.getCompletedTaskCount());
		System.out.println();
	}
	
	public void endServer() {
		executor.shutdown();
	}
}
複製程式碼

Task類用來建立多個runnable物件

package CreateExecutor;

import java.util.Date;
import java.util.concurrent.TimeUnit;

public class Task implements Runnable {

	private Date initDate;
	
	private String name;
	
	
	
	public Task(Date initDate, String name) {
		super();
		this.initDate = initDate;
		this.name = name;
	}



	@Override
	public void run() {
		
		System.out.printf("%s : Task %s : Created on : %s\n",
				Thread.currentThread().getName(),name,initDate);
		System.out.printf("%s : Task %s : Started on : %s\n",
				Thread.currentThread().getName(),name, new Date());
		
		//將任務休眠一段時間,模擬任務的執行
		try {
		Long duration = (long)(Math.random() * 10);
		System.out.printf("%s : Task %s : Doing a task during %d seconds\n",
				Thread.currentThread().getName(), name, duration);
		
		TimeUnit.SECONDS.sleep(duration);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		System.out.printf("%s : Task %s : Finished on : %s\n",
				Thread.currentThread().getName(), name, new Date());
	}
}

複製程式碼

Main類測試:

package CreateExecutor;

import java.util.Date;

public class Main {

	public static void main(String[] args) {
		Server server = new Server();
		for(int i=0;i<10;i++) {
			Task task = new Task(new Date(), "Task " + i);
			server.executorTask(task);
		}
		server.endServer();
	}

}

複製程式碼

執行結果

a new task arrived
Server : Pool Size :1
pool-1-thread-1 : Task Task 0 : Created on : Mon Jul 24 20:31:10 CST 2017
pool-1-thread-1 : Task Task 0 : Started on : Mon Jul 24 20:31:11 CST 2017
Server : ActiveCount :1
Server : CompletedTaskCount :0

a new task arrived
Server : Pool Size :2
Server : ActiveCount :2
Server : CompletedTaskCount :0

a new task arrived
Server : Pool Size :3
Server : ActiveCount :3
Server : CompletedTaskCount :0

a new task arrived
Server : Pool Size :4
Server : ActiveCount :4
Server : CompletedTaskCount :0

a new task arrived
Server : Pool Size :5
Server : ActiveCount :5
Server : CompletedTaskCount :0

pool-1-thread-2 : Task Task 1 : Created on : Mon Jul 24 20:31:11 CST 2017
pool-1-thread-2 : Task Task 1 : Started on : Mon Jul 24 20:31:11 CST 2017
pool-1-thread-5 : Task Task 4 : Created on : Mon Jul 24 20:31:11 CST 2017
pool-1-thread-4 : Task Task 3 : Created on : Mon Jul 24 20:31:11 CST 2017
pool-1-thread-3 : Task Task 2 : Created on : Mon Jul 24 20:31:11 CST 2017
pool-1-thread-1 : Task Task 0 : Doing a task during 3 seconds
pool-1-thread-3 : Task Task 2 : Started on : Mon Jul 24 20:31:11 CST 2017
pool-1-thread-3 : Task Task 2 : Doing a task during 8 seconds
pool-1-thread-4 : Task Task 3 : Started on : Mon Jul 24 20:31:11 CST 2017
pool-1-thread-4 : Task Task 3 : Doing a task during 7 seconds
pool-1-thread-5 : Task Task 4 : Started on : Mon Jul 24 20:31:11 CST 2017
pool-1-thread-2 : Task Task 1 : Doing a task during 9 seconds
pool-1-thread-5 : Task Task 4 : Doing a task during 1 seconds
pool-1-thread-5 : Task Task 4 : Finished on : Mon Jul 24 20:31:12 CST 2017
pool-1-thread-1 : Task Task 0 : Finished on : Mon Jul 24 20:31:14 CST 2017
pool-1-thread-4 : Task Task 3 : Finished on : Mon Jul 24 20:31:18 CST 2017
pool-1-thread-3 : Task Task 2 : Finished on : Mon Jul 24 20:31:19 CST 2017
pool-1-thread-2 : Task Task 1 : Finished on : Mon Jul 24 20:31:20 CST 2017
複製程式碼

Executor的一個特性,必須通過顯示的結束,如果不這麼做,Executor會繼續執行,執行緒也不會結束,程式也不會結束。如果Executor沒有任務可執行了,它不會結束,會一直等待新的任務到來,而不會結束執行。

建立固定大小的執行緒Executor

上面的例子,對五個任務新生成了5個執行緒,為了重複利用執行緒,我們可以建立固定的執行緒數,Executors工廠類就提供了這麼一個工廠方法。 這個Executor會有一個最大的執行緒最大數,如果傳送超過這個任務數的任務給Executor,執行器不會再建立額外的執行緒,剩下的任務將被阻塞直到Executor有足夠的空閒的執行緒可用。

package CreateFixedExecutor;

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class Server {
	private ThreadPoolExecutor executor;

	public Server() {
		executor = (ThreadPoolExecutor)Executors.newFixedThreadPool(5);
	}
	
	public void executorTask(Task task) {
		System.out.println("a new task arrived\n");
		executor.execute(task);
		System.out.println("Server : Pool Size :" + executor.getPoolSize());
		System.out.println("Server : ActiveCount :" + executor.getActiveCount());
		System.out.println("Server : TaskCount :" + executor.getTaskCount());
		System.out.println("Server : CompletedTaskCount :" + executor.getCompletedTaskCount());
	}
	
	public void endServer() {
		executor.shutdown();
	}
}

複製程式碼
package CreateFixedExecutor;

import java.util.Date;
import java.util.concurrent.TimeUnit;

public class Task implements Runnable {

	private Date initDate;
	
	private String name;
	
	
	
	public Task(Date initDate, String name) {
		super();
		this.initDate = initDate;
		this.name = name;
	}



	@Override
	public void run() {
		
		System.out.printf("%s : Task %s : Created on : %s\n",
				Thread.currentThread().getName(),name,initDate);
		System.out.printf("%s : Task %s : Started on : %s\n",
				Thread.currentThread().getName(),name, new Date());
		
		//將任務休眠一段時間,模擬任務的執行
		try {
		Long duration = (long)(Math.random() * 10);
		System.out.printf("%s : Task %s : Doing a task during %d seconds\n",
				Thread.currentThread().getName(), name, duration);
		
		TimeUnit.SECONDS.sleep(duration);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		System.out.printf("%s : Task %s : Finished on : %s\n",
				Thread.currentThread().getName(), name, new Date());
	}
}

複製程式碼
package CreateFixedExecutor;

import java.util.Date;

public class Main {

	public static void main(String[] args) {
		Server server = new Server();
		for(int i=0;i<10;i++) {
			Task task = new Task(new Date(), "Task " + i);
			server.executorTask(task);
		}
		server.endServer();
	}

}

複製程式碼

執行結果:

a new task arrived

Server : Pool Size :1
pool-1-thread-1 : Task Task 0 : Created on : Mon Jul 24 20:40:07 CST 2017
Server : ActiveCount :1
Server : TaskCount :1
Server : CompletedTaskCount :0
a new task arrived

pool-1-thread-1 : Task Task 0 : Started on : Mon Jul 24 20:40:07 CST 2017
Server : Pool Size :2
Server : ActiveCount :2
Server : TaskCount :2
Server : CompletedTaskCount :0
a new task arrived

Server : Pool Size :3
Server : ActiveCount :3
Server : TaskCount :3
Server : CompletedTaskCount :0
a new task arrived

Server : Pool Size :4
Server : ActiveCount :4
Server : TaskCount :4
Server : CompletedTaskCount :0
a new task arrived

Server : Pool Size :5
Server : ActiveCount :5
Server : TaskCount :5
Server : CompletedTaskCount :0
a new task arrived

Server : Pool Size :5
Server : ActiveCount :5
Server : TaskCount :6
Server : CompletedTaskCount :0
a new task arrived

Server : Pool Size :5
pool-1-thread-2 : Task Task 1 : Created on : Mon Jul 24 20:40:07 CST 2017
pool-1-thread-2 : Task Task 1 : Started on : Mon Jul 24 20:40:07 CST 2017
Server : ActiveCount :5
pool-1-thread-2 : Task Task 1 : Doing a task during 7 seconds
pool-1-thread-4 : Task Task 3 : Created on : Mon Jul 24 20:40:07 CST 2017
pool-1-thread-4 : Task Task 3 : Started on : Mon Jul 24 20:40:07 CST 2017
pool-1-thread-1 : Task Task 0 : Doing a task during 9 seconds
pool-1-thread-3 : Task Task 2 : Created on : Mon Jul 24 20:40:07 CST 2017
pool-1-thread-3 : Task Task 2 : Started on : Mon Jul 24 20:40:07 CST 2017
pool-1-thread-4 : Task Task 3 : Doing a task during 2 seconds
pool-1-thread-5 : Task Task 4 : Created on : Mon Jul 24 20:40:07 CST 2017
pool-1-thread-5 : Task Task 4 : Started on : Mon Jul 24 20:40:07 CST 2017
pool-1-thread-5 : Task Task 4 : Doing a task during 5 seconds
Server : TaskCount :7
pool-1-thread-3 : Task Task 2 : Doing a task during 4 seconds
Server : CompletedTaskCount :0
a new task arrived

Server : Pool Size :5
Server : ActiveCount :5
Server : TaskCount :8
Server : CompletedTaskCount :0
a new task arrived

Server : Pool Size :5
Server : ActiveCount :5
Server : TaskCount :9
Server : CompletedTaskCount :0
a new task arrived

Server : Pool Size :5
Server : ActiveCount :5
Server : TaskCount :10
Server : CompletedTaskCount :0
pool-1-thread-4 : Task Task 3 : Finished on : Mon Jul 24 20:40:09 CST 2017
pool-1-thread-4 : Task Task 5 : Created on : Mon Jul 24 20:40:07 CST 2017
pool-1-thread-4 : Task Task 5 : Started on : Mon Jul 24 20:40:09 CST 2017
pool-1-thread-4 : Task Task 5 : Doing a task during 4 seconds
pool-1-thread-3 : Task Task 2 : Finished on : Mon Jul 24 20:40:11 CST 2017
pool-1-thread-3 : Task Task 6 : Created on : Mon Jul 24 20:40:07 CST 2017
pool-1-thread-3 : Task Task 6 : Started on : Mon Jul 24 20:40:11 CST 2017
pool-1-thread-3 : Task Task 6 : Doing a task during 4 seconds
pool-1-thread-5 : Task Task 4 : Finished on : Mon Jul 24 20:40:12 CST 2017
pool-1-thread-5 : Task Task 7 : Created on : Mon Jul 24 20:40:07 CST 2017
pool-1-thread-5 : Task Task 7 : Started on : Mon Jul 24 20:40:12 CST 2017
pool-1-thread-5 : Task Task 7 : Doing a task during 1 seconds
pool-1-thread-5 : Task Task 7 : Finished on : Mon Jul 24 20:40:13 CST 2017
pool-1-thread-5 : Task Task 8 : Created on : Mon Jul 24 20:40:07 CST 2017
pool-1-thread-5 : Task Task 8 : Started on : Mon Jul 24 20:40:13 CST 2017
pool-1-thread-5 : Task Task 8 : Doing a task during 4 seconds
pool-1-thread-4 : Task Task 5 : Finished on : Mon Jul 24 20:40:13 CST 2017
pool-1-thread-4 : Task Task 9 : Created on : Mon Jul 24 20:40:07 CST 2017
pool-1-thread-4 : Task Task 9 : Started on : Mon Jul 24 20:40:13 CST 2017
pool-1-thread-4 : Task Task 9 : Doing a task during 9 seconds
pool-1-thread-2 : Task Task 1 : Finished on : Mon Jul 24 20:40:14 CST 2017
pool-1-thread-3 : Task Task 6 : Finished on : Mon Jul 24 20:40:15 CST 2017
pool-1-thread-1 : Task Task 0 : Finished on : Mon Jul 24 20:40:16 CST 2017
pool-1-thread-5 : Task Task 8 : Finished on : Mon Jul 24 20:40:17 CST 2017
pool-1-thread-4 : Task Task 9 : Finished on : Mon Jul 24 20:40:22 CST 2017

複製程式碼

相關文章