多執行緒(四)

Kakarotto發表於2018-05-06

1、Executor

執行緒池頂級介面。定義方法,void execute(Runnable)。方法是用於處理任務的一個服務 方法。呼叫者提供 Runnable 介面的實現,執行緒池通過執行緒執行這個 Runnable。服務方法無 返回值的。是 Runnable 介面中的 run 方法無返回值。 常用方法 - void execute(Runnable) 作用是: 啟動執行緒任務的。

2、ExecutorService

Executor 介面的子介面。提供了一個新的服務方法,submit。有返回值(Future 型別)。 submit 方法提供了 overload 方法。其中有引數型別為 Runnable 的,不需要提供返回值的; 有引數型別為 Callable,可以提供執行緒執行後的返回值。 Future,是 submit 方法的返回值。代表未來,也就是執行緒執行結束後的一種結果。如返 回值。 常見方法 - void execute(Runnable), Future submit(Callable), Future submit(Runnable) 執行緒池狀態: Running, ShuttingDown, Termitnaed Running - 執行緒池正在執行中。活動狀態。 ShuttingDown - 執行緒池正在關閉過程中。優雅關閉。一旦進入這個狀態,執行緒池不再接 收新的任務,處理所有已接收的任務,處理完畢後,關閉執行緒池。 Terminated - 執行緒池已經關閉。

3、Callable

可執行介面。 類似 Runnable 介面。也是可以啟動一個執行緒的介面。其中定義的方法是 call。call 方法的作用和 Runnable 中的 run 方法完全一致。call 方法有返回值。 介面方法 : Object call();相當於 Runnable 介面中的 run 方法。區別為此方法有返回值。 不能丟擲已檢查異常。 和 Runnable 介面的選擇 - 需要返回值或需要丟擲異常時,使用 Callable,其他情況可 任意選擇。

4、Executors

工具型別。為 Executor 執行緒池提供工具方法。可以快速的提供若干種執行緒池。如:固定 容量的,無限容量的,容量為 1 等各種執行緒池。 執行緒池是一個程式級的重量級資源。預設的生命週期和 JVM 一致。當開啟執行緒池後, 直到 JVM 關閉為止,是執行緒池的預設生命週期。如果手工呼叫 shutdown 方法,那麼執行緒池 執行所有的任務後,自動關閉。 開始 - 建立執行緒池。 結束 - JVM 關閉或呼叫 shutdown 並處理完所有的任務。 類似 Arrays,Collections 等工具型別的功用。

5、newFixedThreadPool

固定容量的執行緒池

容量固定的執行緒池。活動狀態和執行緒池容量是有上限的執行緒池。所有的執行緒池中,都有 一個任務佇列。使用的是 BlockingQueue作為任務的載體。當任務數量大於執行緒 池容量的時候,沒有執行的任務儲存在任務佇列中,當執行緒有空閒的,自動從佇列中取出任 務執行。 使用場景: 大多數情況下,使用的執行緒池,首選推薦 FixedThreadPool。OS 系統和硬體 是有執行緒支援上限。不能隨意的無限制提供執行緒池。 執行緒池預設的容量上限是 Integer.MAX_VALUE。 常見的執行緒池容量: PC - 200。 伺服器 - 1000~10000 queued tasks - 任務佇列 completed tasks - 結束任務佇列

/**
 * 執行緒池
 * 固定容量執行緒池
 * FixedThreadPool - 固定容量執行緒池。建立執行緒池的時候,容量固定。
 *  構造的時候,提供執行緒池最大容量
 * new xxxxx -> 
 * ExecutorService - 執行緒池服務型別。所有的執行緒池型別都實現這個介面。
 *  實現這個介面,代表可以提供執行緒池能力。
 *  shutdown - 優雅關閉。 不是強行關閉執行緒池,回收執行緒池中的資源。而是不再處理新的任務,將已接收的任務處理完畢後
 *      再關閉。
 * Executors - Executor的工具類。類似Collection和Collections的關係。
 *  可以更簡單的建立若干種執行緒池。
 */
package concurrent.t08;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Test_02_FixedThreadPool {
 
 public static void main(String[] args) {
  ExecutorService service = 
    Executors.newFixedThreadPool(5);
  for(int i = 0; i < 6; i++){
   service.execute(new Runnable() {
    @Override
    public void run() {
     try {
      TimeUnit.MILLISECONDS.sleep(500);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
     System.out.println(Thread.currentThread().getName() + " - test executor");
    }
   });
  }
  
  System.out.println(service);
  
  service.shutdown();
  // 是否已經結束, 相當於回收了資源。
  System.out.println(service.isTerminated());
  // 是否已經關閉, 是否呼叫過shutdown方法
  System.out.println(service.isShutdown());
  System.out.println(service);
  
  try {
   TimeUnit.SECONDS.sleep(2);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  
  // service.shutdown();
  System.out.println(service.isTerminated());
  System.out.println(service.isShutdown());
  System.out.println(service);
 }

}
複製程式碼

結果:

java.util.concurrent.ThreadPoolExecutor@42a57993[Running, pool size = 5, active threads = 5, queued tasks = 1, completed tasks = 0]
false
true
java.util.concurrent.ThreadPoolExecutor@42a57993[Shutting down, pool size = 5, active threads = 5, queued tasks = 1, completed tasks = 0]
pool-1-thread-2 - test executor
pool-1-thread-1 - test executor
pool-1-thread-5 - test executor
pool-1-thread-4 - test executor
pool-1-thread-3 - test executor
pool-1-thread-2 - test executor
true
true
java.util.concurrent.ThreadPoolExecutor@42a57993[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 6]
複製程式碼

6、future

未來結果,代表執行緒任務執行結束後的結果。獲取執行緒執行結果的方式是通過 get 方法 獲取的。get 無參,阻塞等待執行緒執行結束,並得到結果。get 有參,阻塞固定時長,等待 執行緒執行結束後的結果,如果在阻塞時長範圍內,執行緒未執行結束,丟擲異常。 常用方法: T get() T get(long, TimeUnit)

/**
 * 執行緒池
 * 固定容量執行緒池
 */
package concurrent.t08;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

public class Test_03_Future {
	
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		/*FutureTask<String> task = new FutureTask<>(new Callable<String>() {
			@Override
			public String call() throws Exception {
				return "first future task";
			}
		});
		
		new Thread(task).start();
		
		System.out.println(task.get());*/
		
		ExecutorService service = Executors.newFixedThreadPool(1);
		
		Future<String> future = service.submit(new Callable<String>() {
			@Override
			public String call() {
				try {
					TimeUnit.MILLISECONDS.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("aaa");
				return Thread.currentThread().getName() + " - test executor";
			}
		});
		System.out.println(future);
		System.out.println(future.isDone()); // 檢視執行緒是否結束, 任務是否完成。 call方法是否執行結束
		
		System.out.println(future.get()); // 獲取call方法的返回值。
		System.out.println(future.isDone());
	}

}
複製程式碼

結果:

java.util.concurrent.FutureTask@6d6f6e28
false
aaa
pool-1-thread-1 - test executor
true
複製程式碼

7、CachedThreadPool

快取的執行緒池。容量不限(Integer.MAX_VALUE)。自動擴容。容量管理策略:如果執行緒 池中的執行緒數量不滿足任務執行,建立新的執行緒。每次有新任務無法即時處理的時候,都會 建立新的執行緒。當執行緒池中的執行緒空閒時長達到一定的臨界值(預設 60 秒),自動釋放執行緒。 預設執行緒空閒 60 秒,自動銷燬。 應用場景: 內部應用或測試應用。 內部應用,有條件的內部資料瞬間處理時應用,如: 電信平臺夜間執行資料整理(有把握在短時間內處理完所有工作,且對硬體和軟體有足夠的 信心)。 測試應用,在測試的時候,嘗試得到硬體或軟體的最高負載量,用於提供 FixedThreadPool 容量的指導。

/**
 * 執行緒池
 * 無容量限制的執行緒池(最大容量預設為Integer.MAX_VALUE)
 */
package concurrent.t08;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Test_05_CachedThreadPool {
	
	public static void main(String[] args) {
		ExecutorService service = Executors.newCachedThreadPool();
		System.out.println(service);
		
		for(int i = 0; i < 5; i++){
			service.execute(new Runnable() {
				@Override
				public void run() {
					try {
						TimeUnit.MILLISECONDS.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + " - test executor");
				}
			});
		}
		
		System.out.println(service);
		
		try {
			TimeUnit.SECONDS.sleep(65);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		System.out.println(service);
	}

}
複製程式碼

結果:

java.util.concurrent.ThreadPoolExecutor@7f31245a[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
java.util.concurrent.ThreadPoolExecutor@7f31245a[Running, pool size = 5, active threads = 5, queued tasks = 0, completed tasks = 0]
pool-1-thread-5 - test executor
pool-1-thread-2 - test executor
pool-1-thread-4 - test executor
pool-1-thread-1 - test executor
pool-1-thread-3 - test executor
java.util.concurrent.ThreadPoolExecutor@7f31245a[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 5]

Process finished with exit code 0
複製程式碼

8、ScheduledThreadPool

計劃任務執行緒池。可以根據計劃自動執行任務的執行緒池。 scheduleAtFixedRate(Runnable, start_limit, limit, timeunit) runnable - 要執行的任務。 start_limit - 第一次任務執行的間隔。 limit - 多次任務執行的間隔。 timeunit - 多次任務執行間隔的時間單位。 使用場景: 計劃任務時選用(DelaydQueue),如:電信行業中的資料整理,沒分鐘整 理,沒小時整理,每天整理等。

/**
 * 執行緒池
 * 計劃任務執行緒池。
 */
package concurrent.t08;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Test_07_ScheduledThreadPool {
	
	public static void main(String[] args) {
		ScheduledExecutorService service = Executors.newScheduledThreadPool(3);
		System.out.println(service);
		
		// 定時完成任務。 scheduleAtFixedRate(Runnable, start_limit, limit, timeunit)
		// runnable - 要執行的任務。
		service.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				try {
					TimeUnit.MILLISECONDS.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName());
			}
		}, 0, 300, TimeUnit.MILLISECONDS);
		
	}

}
複製程式碼

結果:

java.util.concurrent.ScheduledThreadPoolExecutor@14ae5a5[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
pool-1-thread-1
pool-1-thread-1
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
複製程式碼

9、SingleThreadExceutor

單一容量的執行緒池。

使用場景: 保證任務順序時使用。如: 遊戲大廳中的公共頻道聊天。秒殺。

/**
 * 執行緒池
 * 容量為1的執行緒池。 順序執行。
 */
package concurrent.t08;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Test_06_SingleThreadExecutor {
	
	public static void main(String[] args) {
		ExecutorService service = Executors.newSingleThreadExecutor();
		System.out.println(service);
		
		for(int i = 0; i < 5; i++){
			service.execute(new Runnable() {
				@Override
				public void run() {
					try {
						TimeUnit.MILLISECONDS.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + " - test executor");
				}
			});
		}
		
	}

}
複製程式碼

結果:

java.util.concurrent.Executors$FinalizableDelegatedExecutorService@14ae5a5
pool-1-thread-1 - test executor
pool-1-thread-1 - test executor
pool-1-thread-1 - test executor
pool-1-thread-1 - test executor
pool-1-thread-1 - test executor
複製程式碼

10、ForkJoinPool

分支合併執行緒池(mapduce 類似的設計思想)。適合用於處理複雜任務。 初始化執行緒容量與 CPU 核心數相關。 執行緒池中執行的內容必須是 ForkJoinTask 的子型別(RecursiveTask,RecursiveAction)。

ForkJoinPool - 分支合併執行緒池。 可以遞迴完成複雜任務。要求可分支合併的任務必須是 ForkJoinTask 型別的子型別。其中提供了分支和合並的能力。ForkJoinTask 型別提供了兩個抽象子型別,RecursiveTask 有返回結果的分支合併任務,RecursiveAction 無返回結果的分支合併任務。(Callable/Runnable)compute 方法:就是任務的執行邏輯。

ForkJoinPool 沒有所謂的容量。預設都是 1 個執行緒。根據任務自動的分支新的子執行緒。當子執行緒任務結束後,自動合併。所謂自動是根據 fork 和 join 兩個方法實現的。應用: 主要是做科學計算或天文計算的。資料分析的

/**
 * 執行緒池
 * 分支合併執行緒池。
 */
package concurrent.t08;

import java.io.IOException;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;

public class Test_08_ForkJoinPool {
	
	final static int[] numbers = new int[1000000];
	final static int MAX_SIZE = 50000;
	final static Random r = new Random();
	
	
	static{
		for(int i = 0; i < numbers.length; i++){
			numbers[i] = r.nextInt(1000);
		}
	}
	
	static class AddTask extends RecursiveTask<Long>{ // RecursiveAction
		int begin, end;
		public AddTask(int begin, int end){
			this.begin = begin;
			this.end = end;
		}
		
		// 
		protected Long compute(){
			if((end - begin) < MAX_SIZE){
				long sum = 0L;
				for(int i = begin; i < end; i++){
					sum += numbers[i];
				}
				// System.out.println("form " + begin + " to " + end + " sum is : " + sum);
				return sum;
			}else{
				int middle = begin + (end - begin)/2;
				AddTask task1 = new AddTask(begin, middle);
				AddTask task2 = new AddTask(middle, end);
				task1.fork();// 就是用於開啟新的任務的。 就是分支工作的。 就是開啟一個新的執行緒任務。
				task2.fork();
				// join - 合併。將任務的結果獲取。 這是一個阻塞方法。一定會得到結果資料。
				return task1.join() + task2.join();
			}
		}
	}
	
	public static void main(String[] args) throws InterruptedException, ExecutionException, IOException {
		long result = 0L;
		for(int i = 0; i < numbers.length; i++){
			result += numbers[i];
		}
		System.out.println(result);
		
		ForkJoinPool pool = new ForkJoinPool();
		AddTask task = new AddTask(0, numbers.length);
		
		Future<Long> future = pool.submit(task);
		System.out.println(future.get());
		
	}

}
複製程式碼

結果:

499664397
499664397
複製程式碼

相關文章