ExecutorService- Future - Java多线程编程
生活随笔
收集整理的這篇文章主要介紹了
ExecutorService- Future - Java多线程编程
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
JAVA語言內(nèi)置了多線程的支持:1. 但是創(chuàng)建線程需要操作系統(tǒng)的資源(例如線程本身的資源,棧的空間等等)2. 如果我們頻繁的創(chuàng)建和銷毀線程,就需要消耗大量的時(shí)間.
如果我們可以復(fù)用一個(gè)線程,假如我們有大量的小任務(wù),我們就可以讓它們排隊(duì)執(zhí)行,然后在一個(gè)線程池里,由少量的線程執(zhí)行大量的任務(wù).
線程池:1. 所以線程池可以維護(hù)若干個(gè)線程,讓他們處于等待狀態(tài).2. 如果有新任務(wù),就分配一個(gè)空閑的線程來執(zhí)行這個(gè)任務(wù).3. 如果所有的線程都處于忙碌狀態(tài),新任務(wù)就放入隊(duì)列等待.
ExecutorService來表示一個(gè)線程池,我們通常使用ExecutorService的靜態(tài)方法,比如newFixedThreadPool來創(chuàng)建一個(gè)固定大小的線程池,然后我們通過一個(gè)submi()方法提交一個(gè)任務(wù)JDK提供的常用的ExecutorService包括:1. FixedThreadPool: 線程數(shù)固定的線程池2. CachedThreadPool: 線程數(shù)根據(jù)任務(wù)動(dòng)態(tài)調(diào)整的線程池3. SingleThreadExecutor: 這個(gè)線程池只包含一個(gè)線程,也就是所有的任務(wù)只能以單線程的形式執(zhí)行
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方法中我們執(zhí)行多次,大概需要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方法中我們執(zhí)行多次,大概需要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// 這個(gè)時(shí)候我們發(fā)現(xiàn)所有的任務(wù)都是以串行的方式執(zhí)行的,因?yàn)槲覀兊木€程池里面只有一個(gè)線程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方法中我們執(zhí)行多次,大概需要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會根據(jù)我們的任務(wù)動(dòng)態(tài)的調(diào)整線程的數(shù)量// 所以這四個(gè)任務(wù)提交進(jìn)去以后,線程池會立刻創(chuàng)建四個(gè)線程,// 如果我們想要限制線程池的上限,最多10個(gè)線程,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的源碼,實(shí)際上就是一個(gè)ThreadPoolExecutor的實(shí)例,我們可以復(fù)制一下代碼.第一個(gè)參數(shù)是corePoolSize,第二個(gè)參數(shù)maximunPoolSize線程池最大的大小,我們可以把第二個(gè)參數(shù)調(diào)整為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方法中我們執(zhí)行多次,大概需要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個(gè)線程的線程池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還提供了一個(gè)ScheduledThreadPool1. 它可以把一個(gè)任務(wù)定期的反復(fù)的執(zhí)行ScheduledThreadPool他又兩種執(zhí)行模式
一種是 Fixed Rate一種是 Fixed DelayFixed Rate 是指在固定的間隔任務(wù)就會執(zhí)行,例如每隔三秒鐘任務(wù)就會啟動(dòng),而不管任務(wù)到底執(zhí)行了多長時(shí)間.Fixed Delay 是指任務(wù)執(zhí)行完畢以后,我們等待一秒鐘,再接著執(zhí)行.無論任務(wù)執(zhí)行多久,只有在任務(wù)執(zhí)行結(jié)束以后,等待一秒鐘,才會執(zhí)行下一次的任務(wù).
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秒鐘* 所以執(zhí)行這個(gè)任務(wù)大概需要一秒* @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) {// 先創(chuàng)建一個(gè)Schedule的ExecutorService,然后提交兩個(gè)任務(wù)ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);// 第一個(gè)任務(wù)我們以FixedRate的模式執(zhí)行,2表示兩秒以后執(zhí)行,參數(shù)5表示每隔5秒執(zhí)行這個(gè)任務(wù)executor.scheduleAtFixedRate(new HelloTask("Bob"), 2, 5, TimeUnit.SECONDS);// 第2個(gè)任務(wù)我們以fixedDelay來運(yùn)行,參數(shù)2表示我們會在2秒以后來執(zhí)行,參數(shù)5表示間隔是5秒鐘executor.scheduleWithFixedDelay(new HelloTask("Alice"), 2, 5, TimeUnit.SECONDS);}}由于Schedule的ThreadPool不會自動(dòng)停止,所以我們強(qiáng)制結(jié)束虛擬機(jī)我們觀察執(zhí)行的結(jié)果,我們可以看到Hello Bob的會執(zhí)行的比較快,每隔5秒鐘就會執(zhí)行,而Hello Alice這個(gè)任務(wù),它執(zhí)行的任務(wù)會比較低,因?yàn)樗鼤g隔5秒鐘才會執(zhí)行
1. 我們使用ScheduledThreadPool會有一個(gè)問題,在FixedRate模式下,如果執(zhí)行的任務(wù)時(shí)間過長,后續(xù)的任務(wù)會不會導(dǎo)致并發(fā)執(zhí)行呢?2. 如果任務(wù)拋出了異常,后續(xù)任務(wù)是否還是繼續(xù)執(zhí)行呢?大家可以通過代碼簡單的驗(yàn)證一下
JDK還提供了一個(gè)java.util.Timer的類這個(gè)類也可以定期的執(zhí)行一個(gè)任務(wù)1. 一個(gè)Timer類只會對應(yīng)一個(gè)Thread類,所以只會定期執(zhí)行一個(gè)任務(wù)2. 如果我們要執(zhí)行多個(gè)定期任務(wù),就必須啟動(dòng)多個(gè)Timer,而一個(gè)ScheduledThreadPool就可以調(diào)度多個(gè)任務(wù)所以我們現(xiàn)在完全可以使用ScheduledThreadPool取代舊的Timer類
1. JDK提供的ExecutorService實(shí)現(xiàn)了線程池的功能2. 線程池內(nèi)部維護(hù)一組線程,可以高效執(zhí)行大量的小任務(wù)3. Executors提供了靜態(tài)方法創(chuàng)建不同類型的ExecutorService4. 必須調(diào)用shutdown()關(guān)閉ExecutorService5. ScheduleThreadPool可以定期調(diào)度多個(gè)任務(wù)
但是這里我們提交一個(gè)Task,因?yàn)樗^承自Runnable
因此JDK又提供了一個(gè)Callable接口
我們可以把一個(gè)Callable的Task交給Executor異步執(zhí)行,當(dāng)我們實(shí)現(xiàn)Callable接口的時(shí)候,我們需要實(shí)現(xiàn)一個(gè)泛型接口,例如我們期望的返回值是一個(gè)String,我們就寫Callable<String>,我們要復(fù)寫call方法
我們?nèi)绾潍@取一個(gè)異步執(zhí)行的結(jié)果呢?當(dāng)我們提交一個(gè)Callable的任務(wù)以后,我們會獲得一個(gè)Future<String>對象,然后我們在主線程的某個(gè)時(shí)刻調(diào)用future對象的get()方法,如果異步結(jié)果完成,我們就直接獲得結(jié)果,如果異步任務(wù)還沒有完成,那么get方法會阻塞,直到任務(wù)完成以后才會有結(jié)果.
Future接口表示未來可能會返回的結(jié)果,通過get方法返回一個(gè)異步返回的結(jié)果cacel方法會終止一個(gè)異步任務(wù)的執(zhí)行,isDone()方法可以判斷當(dāng)前的異步任務(wù)是否已經(jīng)完成.
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;/*** 有一個(gè)Callable接口* @author Leon.Sun**/
class DownloadTask implements Callable<String> {String url;public DownloadTask(String url) {this.url = url;}// 我們復(fù)寫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 {// 我們先創(chuàng)建一個(gè)ExecutorExecutorService executor = Executors.newFixedThreadPool(3);// 我們可以下載一個(gè)指定URL的網(wǎng)頁// 我們創(chuàng)建一個(gè)DownloadTask,傳入一個(gè)URLDownloadTask task = new DownloadTask("http://www.baidu.com/");// 最后我們把下載好的網(wǎng)頁以String的方式返回// 我們提交這個(gè)任務(wù)Future<String> future = executor.submit(task);// 然后通過future的get方法獲得String html = future.get();System.out.println(html);// 關(guān)閉這個(gè)線程池executor.shutdown();}
}
當(dāng)我們不需要返回結(jié)果的時(shí)候,我們可以提交一個(gè)Runnable的任務(wù),如果我們需要一個(gè)返回結(jié)果的任務(wù)的時(shí)候我們就提交一個(gè)Callable的任務(wù)
1. 提交一個(gè)Callable的任務(wù),可以獲得一個(gè)Future對象2. 可以用Future在將來某個(gè)時(shí)刻獲取結(jié)果
?
總結(jié)
以上是生活随笔為你收集整理的ExecutorService- Future - Java多线程编程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Concurrent集合 Atomic
- 下一篇: Fork_Join - Java多线程编