最近項目使用原生的多線程使用方式,有點遺忘,趕緊寫個demo溫習一下。
一、Executors
在Java用有一個Executors工具類,可以為我們創建一個線程池,其本質就是new了一個ThreadPoolExecutor對象。
建議使用較為方便的 Executors 工廠方法來創建線程池。
Executors.newCachedThreadPool()(無界線程池,可以進行自動線程回收)
Executors.newFixedThreadPool(int)(固定大小線程池)
Executors.newSingleThreadExecutor()(單個后台線程)
Executors.newScheduledThreadPool() (支持計划任務的線程池)
package hanwl.juc.day2; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /* * 一、線程池:提供了一個線程隊列,隊列中保存着所有等待狀態的線程。避免了創建與銷毀額外開銷,提高了響應的速度。 * * 二、線程池的體系結構: * java.util.concurrent.Executor : 負責線程的使用與調度的根接口 * |--**ExecutorService 子接口: 線程池的主要接口 * |--ThreadPoolExecutor 線程池的實現類 * |--ScheduledExecutorService 子接口:負責線程的調度 * |--ScheduledThreadPoolExecutor :繼承 ThreadPoolExecutor, 實現 ScheduledExecutorService * * 三、工具類 : Executors * ExecutorService newFixedThreadPool() : 創建固定大小的線程池 * ExecutorService newCachedThreadPool() : 緩存線程池,線程池的數量不固定,可以根據需求自動的更改數量。 * ExecutorService newSingleThreadExecutor() : 創建單個線程池。線程池中只有一個線程 * * ScheduledExecutorService newScheduledThreadPool() : 創建固定大小的線程,可以延遲或定時的執行任務。 */ public class TestThreadPool { public static void main(String[] args) throws Exception { //1. 創建線程池 ExecutorService pool = Executors.newFixedThreadPool(5); List<Future<Integer>> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { Future<Integer> future = pool.submit(new Callable<Integer>(){ @Override public Integer call() throws Exception { int sum = 0; for (int i = 0; i <= 100; i++) { sum += i; } return sum; } }); list.add(future); } pool.shutdown(); for (Future<Integer> future : list) { System.out.println(future.get()); } /*ThreadPoolDemo tpd = new ThreadPoolDemo(); //2. 為線程池中的線程分配任務 for (int i = 0; i < 10; i++) { pool.submit(tpd); } //3. 關閉線程池 pool.shutdown();*/ } // new Thread(tpd).start(); // new Thread(tpd).start(); } class ThreadPoolDemo implements Runnable{ private int i = 0; @Override public void run() { while(i <= 100){ System.out.println(Thread.currentThread().getName() + " : " + i++); } } }
package hanwl.juc.day2; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class TestScheduledThreadPool { public static void main(String[] args) throws Exception { ScheduledExecutorService pool = Executors.newScheduledThreadPool(5); for (int i = 0; i < 5; i++) { Future<Integer> result = pool.schedule(new Callable<Integer>(){ @Override public Integer call() throws Exception { int num = new Random().nextInt(100);//生成隨機數 System.out.println(Thread.currentThread().getName() + " : " + num); return num; } }, 1, TimeUnit.SECONDS); System.out.println(result.get()); } pool.shutdown(); } }
二、Callable
Callable接口代表一段可以調用並返回結果的代碼;Future接口表示異步任務,是還沒有完成的任務給出的未來結果。所以說Callable用於產生結果,Future用於獲取結果。
Callable接口使用泛型去定義它的返回類型。Executors類提供了一些有用的方法在線程池中執行Callable內的任務。由於Callable任務是並行的(並行就是整體看上去是並行的,其實在某個時間點只有一個線程在執行),我們必須等待它返回的結果。
java.util.concurrent.Future對象為我們解決了這個問題。在線程池提交Callable任務后返回了一個Future對象,使用它可以知道Callable任務的狀態和得到Callable返回的執行結果。Future提供了get()方法讓我們可以等待Callable結束並獲取它的執行結果。
步驟:
- 創建實體類,實現
Callable
接口 - 實現接口中的
call()
方法 - 利用
ExecutorService
線程池對象 的<T> Future<T> submit(Callable<T> task()
方法提交該Callable接口的線程任務。
// 創建線程池 ExecutorService pool = Executors.newFixedThreadPool(2); // 可以執行Runnable對象或者Callable對象代表的線程 Future<Integer> f1 = pool.submit(new MyCallable(100)); Future<Integer> f2 = pool.submit(new MyCallable(200)); // V get() Integer i1 = f1.get(); Integer i2 = f2.get(); System.out.println(i1); System.out.println(i2); // 結束 pool.shutdown(); public class MyCallable implements Callable<Integer> { private int number; public MyCallable(int number) { this.number = number; } @Override public Integer call() throws Exception { int sum = 0; for (int x = 1; x <= number; x++) { sum += x; } return sum; } }
利用匿名內部類方式
ExecutorService service = Executors.newSingleThreadExecutor(); Future<String> future = service.submit(new Callable() { @Override public String call() throws Exception { return "通過實現Callable接口"; } }); try { String result = future.get(); System.out.println(result); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }
Lambda表達式方式
public class CallableTest { public static void main(String[] args) throws ExecutionException, InterruptedException { //使用Executors工廠類創建一個單線程池 ExecutorService es = Executors.newSingleThreadExecutor(); //使用這個單線程提交一個Callable接口線程服務,返回值為String //Callable接口是一個函數式接口,Java8開始可以直接使用Lambda表達式表示 //其內部實現了call()方法 V call() throws Exception; //並得到該結果值打印 System.out.println( es.submit(()->"使用lambda表達式的Callable接口").get()); es.shutdown(); //關閉該線程池 } }