ExecutorService- Future - Java多线程编程
生活随笔
收集整理的這篇文章主要介紹了
ExecutorService- Future - Java多线程编程
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
JAVA語言內置了多線程的支持:1. 但是創建線程需要操作系統的資源(例如線程本身的資源,棧的空間等等)2. 如果我們頻繁的創建和銷毀線程,就需要消耗大量的時間.
如果我們可以復用一個線程,假如我們有大量的小任務,我們就可以讓它們排隊執行,然后在一個線程池里,由少量的線程執行大量的任務.
線程池:1. 所以線程池可以維護若干個線程,讓他們處于等待狀態.2. 如果有新任務,就分配一個空閑的線程來執行這個任務.3. 如果所有的線程都處于忙碌狀態,新任務就放入隊列等待.
ExecutorService來表示一個線程池,我們通常使用ExecutorService的靜態方法,比如newFixedThreadPool來創建一個固定大小的線程池,然后我們通過一個submi()方法提交一個任務JDK提供的常用的ExecutorService包括:1. FixedThreadPool: 線程數固定的線程池2. CachedThreadPool: 線程數根據任務動態調整的線程池3. SingleThreadExecutor: 這個線程池只包含一個線程,也就是所有的任務只能以單線程的形式執行
package com.leon.day05;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;// PrintTask繼承自Runnable
class PrintTask implements Runnable {String name;public PrintTask(String name) {this.name = name;}@Overridepublic void run() {// run方法中我們執行多次,大概需要3秒鐘for(int i=0;i<3;i++) {System.out.println("Hello, " + name + "!");try {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}finally {}}}}public class ThreadPool {public static void main(String[] args) throws InterruptedException {ExecutorService executor = Executors.newFixedThreadPool(3);executor.submit(new PrintTask("Bob"));executor.submit(new PrintTask("Alice"));executor.submit(new PrintTask("Tim"));executor.submit(new PrintTask("Robot"));Thread.sleep(100);executor.shutdown();}}
package com.leon.day05;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;// PrintTask繼承自Runnable
class PrintTask implements Runnable {String name;public PrintTask(String name) {this.name = name;}@Overridepublic void run() {// run方法中我們執行多次,大概需要3秒鐘for(int i=0;i<3;i++) {System.out.println("Hello, " + name + "!");try {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}finally {}}}}public class ThreadPool {public static void main(String[] args) throws InterruptedException {// 我們改成SingleThreadPool// 這個時候我們發現所有的任務都是以串行的方式執行的,因為我們的線程池里面只有一個線程ExecutorService executor = Executors.newSingleThreadExecutor();executor.submit(new PrintTask("Bob"));executor.submit(new PrintTask("Alice"));executor.submit(new PrintTask("Tim"));executor.submit(new PrintTask("Robot"));Thread.sleep(100);executor.shutdown();}}
package com.leon.day05;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;// PrintTask繼承自Runnable
class PrintTask implements Runnable {String name;public PrintTask(String name) {this.name = name;}@Overridepublic void run() {// run方法中我們執行多次,大概需要3秒鐘for(int i=0;i<3;i++) {System.out.println("Hello, " + name + "!");try {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}finally {}}}}public class ThreadPool {public static void main(String[] args) throws InterruptedException {// 我們再改成CachedThreadPool,由于CachedThreadPool會根據我們的任務動態的調整線程的數量// 所以這四個任務提交進去以后,線程池會立刻創建四個線程,// 如果我們想要限制線程池的上限,最多10個線程,ExecutorService executor = Executors.newCachedThreadPool();executor.submit(new PrintTask("Bob"));executor.submit(new PrintTask("Alice"));executor.submit(new PrintTask("Tim"));executor.submit(new PrintTask("Robot"));Thread.sleep(100);executor.shutdown();}}
public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());}cachedThreadPool的源碼,實際上就是一個ThreadPoolExecutor的實例,我們可以復制一下代碼.第一個參數是corePoolSize,第二個參數maximunPoolSize線程池最大的大小,我們可以把第二個參數調整為10
package com.leon.day05;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;// PrintTask繼承自Runnable
class PrintTask implements Runnable {String name;public PrintTask(String name) {this.name = name;}@Overridepublic void run() {// run方法中我們執行多次,大概需要3秒鐘for(int i=0;i<3;i++) {System.out.println("Hello, " + name + "!");try {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}finally {}}}}public class ThreadPool {public static void main(String[] args) throws InterruptedException {// 這里就是10個線程的線程池ExecutorService executor = new ThreadPoolExecutor(0, 10,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());executor.submit(new PrintTask("Bob"));executor.submit(new PrintTask("Alice"));executor.submit(new PrintTask("Tim"));executor.submit(new PrintTask("Robot"));Thread.sleep(100);executor.shutdown();}}
JDK還提供了一個ScheduledThreadPool1. 它可以把一個任務定期的反復的執行ScheduledThreadPool他又兩種執行模式
一種是 Fixed Rate一種是 Fixed DelayFixed Rate 是指在固定的間隔任務就會執行,例如每隔三秒鐘任務就會啟動,而不管任務到底執行了多長時間.Fixed Delay 是指任務執行完畢以后,我們等待一秒鐘,再接著執行.無論任務執行多久,只有在任務執行結束以后,等待一秒鐘,才會執行下一次的任務.
package com.leon.day05;import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;/*** 在HelloTask中打印兩行語句,然后中間間隔1秒鐘* 所以執行這個任務大概需要一秒* @author Leon.Sun**/
class HelloTask implements Runnable {String name;public HelloTask(String name) {this.name = name;}@Overridepublic void run() {System.out.println("Hello, " + name + "! It is " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Goodbye, " + name + "! It is " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));}}public class Schedule {public static void main(String[] args) {// 先創建一個Schedule的ExecutorService,然后提交兩個任務ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);// 第一個任務我們以FixedRate的模式執行,2表示兩秒以后執行,參數5表示每隔5秒執行這個任務executor.scheduleAtFixedRate(new HelloTask("Bob"), 2, 5, TimeUnit.SECONDS);// 第2個任務我們以fixedDelay來運行,參數2表示我們會在2秒以后來執行,參數5表示間隔是5秒鐘executor.scheduleWithFixedDelay(new HelloTask("Alice"), 2, 5, TimeUnit.SECONDS);}}由于Schedule的ThreadPool不會自動停止,所以我們強制結束虛擬機我們觀察執行的結果,我們可以看到Hello Bob的會執行的比較快,每隔5秒鐘就會執行,而Hello Alice這個任務,它執行的任務會比較低,因為它會間隔5秒鐘才會執行
1. 我們使用ScheduledThreadPool會有一個問題,在FixedRate模式下,如果執行的任務時間過長,后續的任務會不會導致并發執行呢?2. 如果任務拋出了異常,后續任務是否還是繼續執行呢?大家可以通過代碼簡單的驗證一下
JDK還提供了一個java.util.Timer的類這個類也可以定期的執行一個任務1. 一個Timer類只會對應一個Thread類,所以只會定期執行一個任務2. 如果我們要執行多個定期任務,就必須啟動多個Timer,而一個ScheduledThreadPool就可以調度多個任務所以我們現在完全可以使用ScheduledThreadPool取代舊的Timer類
1. JDK提供的ExecutorService實現了線程池的功能2. 線程池內部維護一組線程,可以高效執行大量的小任務3. Executors提供了靜態方法創建不同類型的ExecutorService4. 必須調用shutdown()關閉ExecutorService5. ScheduleThreadPool可以定期調度多個任務
但是這里我們提交一個Task,因為它繼承自Runnable
因此JDK又提供了一個Callable接口
我們可以把一個Callable的Task交給Executor異步執行,當我們實現Callable接口的時候,我們需要實現一個泛型接口,例如我們期望的返回值是一個String,我們就寫Callable<String>,我們要復寫call方法
我們如何獲取一個異步執行的結果呢?當我們提交一個Callable的任務以后,我們會獲得一個Future<String>對象,然后我們在主線程的某個時刻調用future對象的get()方法,如果異步結果完成,我們就直接獲得結果,如果異步任務還沒有完成,那么get方法會阻塞,直到任務完成以后才會有結果.
Future接口表示未來可能會返回的結果,通過get方法返回一個異步返回的結果cacel方法會終止一個異步任務的執行,isDone()方法可以判斷當前的異步任務是否已經完成.
package com.leon.day05;import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;/*** 有一個Callable接口* @author Leon.Sun**/
class DownloadTask implements Callable<String> {String url;public DownloadTask(String url) {this.url = url;}// 我們復寫call方法public String call() throws Exception {System.out.println("Start download " + url + "...");URLConnection conn = new URL(this.url).openConnection();conn.connect();try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"))) {String s = null;StringBuilder sb = new StringBuilder();while ((s = reader.readLine()) != null) {sb.append(s).append("\n");}return sb.toString();}}
}public class Main {public static void main(String[] args) throws Exception {// 我們先創建一個ExecutorExecutorService executor = Executors.newFixedThreadPool(3);// 我們可以下載一個指定URL的網頁// 我們創建一個DownloadTask,傳入一個URLDownloadTask task = new DownloadTask("http://www.baidu.com/");// 最后我們把下載好的網頁以String的方式返回// 我們提交這個任務Future<String> future = executor.submit(task);// 然后通過future的get方法獲得String html = future.get();System.out.println(html);// 關閉這個線程池executor.shutdown();}
}
當我們不需要返回結果的時候,我們可以提交一個Runnable的任務,如果我們需要一個返回結果的任務的時候我們就提交一個Callable的任務
1. 提交一個Callable的任務,可以獲得一個Future對象2. 可以用Future在將來某個時刻獲取結果
?
總結
以上是生活随笔為你收集整理的ExecutorService- Future - Java多线程编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Concurrent集合 Atomic
- 下一篇: Fork_Join - Java多线程编