java多執行緒
Java多執行緒——<一>概述、定義任務
一、概述
為什麼使用執行緒?從c開始,任何一門高階語言的預設執行順序是“按照編寫的程式碼的順序執行”,日常開發過程中寫的業務邏輯,但凡不涉及併發的,都是讓一個任務順序執行以確保得到想要的結果。但是,當你的任務需要處理的業務比較多時,且這些業務前後之間沒有依賴(比如, a執行的過程中b也可以執行,b沒有必要必須等待a執行完畢再去執行),那麼此時,我們可以將一個任務拆分成多個小任務。
例如,任務a負責接收鍵盤的輸入,b負責將一些引數及計算提前做好(假設計算量比較大),c負責將a的輸入和b的結果做和。此時,abc順序執行的話,如果a的輸入被阻塞了即正在等待使用者輸入,b就無法執行,而此時cpu處於空置狀態(假設是單cpu且單核),明顯效率不高。
換一個思路,假如:abc分開成為三個任務,a的輸入被阻塞了,那麼此時就把b交給cpu去執行,待使用者輸入結果之後,b已經將計算結果輸出給了c,此時,使用者提交後,c便立即計算出了結果。
綜上:多執行緒解決的是併發的問題,目的是使任務執行效率更高,實現前提是“阻塞”。它們看上去時同時在執行的,但實際上只是分時間片試用cpu而已。
二、java中的多執行緒
1.定義任務
任務:簡單來說,就是一序列工作的集合,這些工作之間有前後順序,這一系列過程執行過後將實現一個結果或達到一個目的。
首先,思考一個問題,為什麼要定義任務?作為java程式設計師,我們不關心底層的多執行緒機制是如何執行的,只關心我寫個怎樣的任務,java的底層的多執行緒機制才能認識,才能呼叫你的任務去執行。java是定義了Runnable介面讓你去實現,意思就是:你實現Runnable介面類定義一個類,該類的物件就是我能識別的任務,其他方式定義的程式,我都不會將它認為是任務。
好,到這裡要明確一點,我們此時只談論任務,不說多執行緒。任務和你平時在一個類中編寫的程式碼並無區別,只是按照java的要求實現了一個介面,並在該介面的run方法中編寫了你的程式碼。也就是說,你平時想編寫一個類,該類能夠完成一些功能,這個類裡的任何方法、變數由你自己來定義,而編寫任務時,你需要實現Runnable介面,把你想讓該任務實現的程式碼寫到run方法中,當然,你可以在你定義的任務類中再定義其他變數、方法以在run中呼叫。
2.程式碼實現
public class Task implements Runnable { protected int countDown = 10; private static int taskCount = 0 ; private final int id = taskCount++; public Task(){} public Task(int countDown){ this.countDown = countDown; } public String status(){ return "#"+id+"("+(countDown>0?countDown:"Task!")+"). "; } @Override public void run() { while(countDown-->0){ System.out.print(status()); Thread.yield(); } } }
注:此處程式碼源於《thinking in java》
定義了任務,此時並不涉及多執行緒,所以,任務本身就是一個類,它的物件我們可以在任意試用到的地方呼叫,例如:
public class TaskMain { public static void main(String[] args){ Task task = new Task(); task.run(); } }
就是在main中宣告瞭該例項的物件,並呼叫了它的run方法,同我們平時建立類一樣來呼叫物件的方法即可。
至此,一個任務定義完了。也就是說按照java的要求,我們實現了一個簡單的任務。然而,實現任務的目的不只是為了實現任務,而是為了讓多執行緒機制能夠呼叫該任務去執行。請看:Java多執行緒——<二>將任務交給執行緒,執行緒宣告
Java多執行緒——<二>將任務交給執行緒,執行緒宣告及啟動
一、任務和執行緒
《thinking in java》中專門有一小節中對執行緒和任務兩個概念進行了具體的區分,這也恰好說明任務和執行緒是有區別的。
正如前文所提到的,任務只是一段程式碼,一段要達成你目的的程式碼,這段程式碼寫在哪,怎麼寫其實無所謂,只是因為你希望java的多執行緒機制能夠識別並呼叫你編寫的任務,所以規定了Runnable介面,讓你的任務來實現該介面,把你想做的工作在實現該介面的run方法中實現。
那麼,已經定義了任務類,那任務和執行緒有什麼關係呢?
java的執行緒是用來驅動任務執行的,也就是說你得把任務掛載到一個執行緒上,這樣該執行緒才能驅動你定義的任務來執行。
二、定義執行緒
1.顯示的定義執行緒的過程就是將任務附著到執行緒的過程。執行緒Thread自身並不執行任何操作,它只是用來被多執行緒機制呼叫,並驅動賦予它的任務。
如前次文章提到的任務類定義如下:
public class Task implements Runnable { protected int countDown = 10; private static int taskCount = 0 ; private final int id = taskCount++; public Task(){} public Task(int countDown){ this.countDown = countDown; } public String status(){ return "#"+id+"("+(countDown>0?countDown:"Task!")+"). "; } @Override public void run() { while(countDown-->0){ System.out.print(status()); Thread.yield(); } } }
宣告執行緒並將任務附著到該執行緒上:
Thread t = new Thread(new Task());
這樣,任務就附著給了執行緒,下面就是讓執行緒啟動,只需要如下的呼叫:
t.start();
至此,執行緒宣告ok。
有時,我會想,是不是像任務和執行緒的概念分離一樣,此時只是宣告瞭執行緒,而java的執行緒機制並不會呼叫該執行緒執行,還需要特殊的呼叫才能實現多執行緒執行。但是下面的一段程式碼告訴我,Thread類的start方法就是觸發了java的多執行緒機制,使得java的多執行緒能夠呼叫該執行緒
public static void main(String[] args){ Thread t = new Thread(new Task()); t.start(); System.out.println("Waiting for Task"); }
輸出結果如下:
Waiting for Task #0(9). #0(8). #0(7). #0(6). #0(5). #0(4). #0(3). #0(2). #0(1). #0(Task!).
先輸出“Waiting for Task”證明呼叫完start()方法後,立即返回了主程式,並開始執行下面的語句。而你宣告的t執行緒已經去被java的多執行緒機制呼叫,並驅動著它的任務執行了。
2.補充
想看到更多的執行緒任務執行,可以用下面的這段程式碼
public static void main(String[] args){ for(int i = 0 ; i < 5 ; i++){ new Thread(new Task()).start(); } System.out.println("Waiting for Task"); }
輸出如下:
Waiting for Task #0(9). #2(9). #4(9). #0(8). #2(8). #4(8). #0(7). #2(7). #4(7). #0(6). #2(6). #4(6). #0(5). #2(5). #4(5). #0(4). #2(4). #4(4). #3(9). #2(3). #4(3). #2(2). #4(2). #2(1). #4(1). #2(Task!). #4(Task!). #1(9). #0(3). #0(2). #0(1). #0(Task!). #3(8). #1(8). #3(7). #1(7). #3(6). #1(6). #3(5). #3(4). #3(3). #3(2). #3(1). #3(Task!). #1(5). #1(4). #1(3). #1(2). #1(1). #1(Task!).
上面的輸出說明不同任務的執行線上程被換進換出時混在了一起——由執行緒排程器自動控制。不同版本的jdk執行緒排程方式不同,所以產生的結果也不相同。
這裡涉及了垃圾回收器的一個問題,每個Thread都註冊了它自己,因此確實有一個對它的引用,而且在它的任務退出其run並死亡之前,垃圾回收器無法清除它。
注:以上程式碼均來自《thinking in java》,內容大部分是個人理解和總結,如有錯誤請各位指正
Java多執行緒——<三>簡單的執行緒執行:Executor
一、概述
按照《Java多執行緒——<一><二>》中所講,我們要使用執行緒,目前都是顯示的宣告Thread,並呼叫其start()方法。多執行緒並行,明顯我們需要宣告多個執行緒然後都呼叫他的start方法,這麼一看,似乎有些問題:第一、執行緒一旦多了,宣告勢必是個問題;第二、多執行緒啟動如果通過手動執行的話,那可能一個執行緒已經跑完了,另外一個還沒起來(我推測可能會出現這個問題)。所以,我們在想,如果有個管家,能夠幫我們管理這麼多執行緒,只需要把我們定義的任務交給管家,管家就能夠幫我們把任務附著到執行緒上,並且當我們給管家傳送指令讓所有的執行緒開始併發執行時,他也能夠幫助我們開啟所有執行緒執行。
二、java多執行緒管家——Executor
Executor允許你管理非同步任務的執行,而無須顯示地管理執行緒的生命週期。ExecutorService知道如何構建恰當的上下文來執行Runnable物件。
1.建立ExecutorService
通過Executors能夠建立兩種方式的ExectorService。第一種、CachedThreadPool會為每個傳入的任務新建立一個執行緒
ExecutorService exec = Executors.newCachedThreadPool();
第二種、FixedThreadPool可以一次性預先執行代價高昂的執行緒分配,所以可以用來限制執行緒的數量。這可以節省時間,因為你不必為每個任務都固定的付出建立執行緒的開銷。
ExecutorService exeService = Executors.newFixedThreadPool(5);
2.把任務附著給ExecutorService
有了executor,你只需要定義任務並將任務物件傳遞給executor即可。
exeService.execute(new Task());
3.讓所有任務開始執行
這兩個方法會讓之前提交到該exectuor的所有任務開始執行。為了避免啟動後,會被注入新的任務,必須在你將所有執行緒注入後,執行關閉操作以保證這一點。
exeService.shutdown();
總結:
|——原來:想執行任務
| | |——1.定義任務
| | |——2.建立任務物件交由Thread物件操縱
| | |——3.顯示的呼叫Thread物件的start()方法
| |——遇到問題:比較繁瑣,總得自己啟動執行緒呼叫;本質上是由main函式呼叫的
|——現在:使用java.util.concurrent.Executor(執行器)來管理Thread物件
| | |——1.ExecutorService exec = Executors.newAcahedThreadPool();
| | |——2.exec.execute(new Task());
| | |——3.exec.shutdown();
| |——在客戶端(顯示呼叫執行緒執行)和執行任務之間提供了一個間接層,用以執行任務;撇開了main函式,由executor直接進行了呼叫
| |——允許你管理非同步任務的執行,而無須顯示地管理執行緒的生命週期
三、其他
1.何時使用哪種執行緒池呢?
CachedThreadPool在程式執行過程中通常會建立與所需數量相同的執行緒,然後在它回收舊執行緒時停止建立新執行緒,因此它是合理的Executor的首選
只有當這種方式會引起問題時,才需要切換到FixedThreadPool。
2.SingleThreadExecutor
SingleThreadExecutor就像是執行緒數量為1的FixedThreadPool
|——在另一個執行緒中連續執行的任何事務來說都很有用(重點是連續執行,因為這樣可以順序接受處理),故SingleThreadPool會序列化所有提交給它的任務, 並會維護它自己隱藏的懸掛任務佇列
|——例如:向SingleThreadExecutor提交多個任務,那麼這些任務將排隊,每個任務都會在下一個任務開始之前執行結束,所有任務使用相同執行緒。
3.自定義執行緒工廠
每個靜態的ExecutorService建立方法都被過載為接受一個ThreadFactory物件,該物件將被用來建立新的執行緒。例如:
public class TaskDaemonFactory implements ThreadFactory{ public Thread newThread(Runnable r){ Thread t = new Thread(r);return t; } }
想使用自己定義的執行緒工廠
ExecutorService exec = Executors.newCachedThreadPool(new TaskDaemonFactory());
這樣可以通過具體的要求來改造執行緒。
相關文章
- Java多執行緒-執行緒中止Java執行緒
- 【Java多執行緒】輕鬆搞定Java多執行緒(二)Java執行緒
- java——多執行緒Java執行緒
- Java - 多執行緒Java執行緒
- java 多執行緒Java執行緒
- Java多執行緒之執行緒中止Java執行緒
- Java多執行緒-執行緒狀態Java執行緒
- Java多執行緒-執行緒通訊Java執行緒
- java 多執行緒守護執行緒Java執行緒
- Java多執行緒(2)執行緒鎖Java執行緒
- java多執行緒9:執行緒池Java執行緒
- 【java多執行緒】(二)執行緒停止Java執行緒
- Java多執行緒學習(一)Java多執行緒入門Java執行緒
- Java多執行緒(一)多執行緒入門篇Java執行緒
- 【Java多執行緒】執行緒安全的集合Java執行緒
- 【Java】【多執行緒】執行緒池簡述Java執行緒
- Java多執行緒-執行緒池的使用Java執行緒
- java 多執行緒CountDownLatchJava執行緒CountDownLatch
- java 多執行緒-3Java執行緒
- java 多執行緒-2Java執行緒
- java 多執行緒 –同步Java執行緒
- java使用多執行緒Java執行緒
- Java--多執行緒Java執行緒
- java 多執行緒 --同步Java執行緒
- java多執行緒原理Java執行緒
- Java多執行緒學習——執行緒通訊Java執行緒
- Java多執行緒學習(2)執行緒控制Java執行緒
- Java 多執行緒基礎(四)執行緒安全Java執行緒
- java多執行緒之執行緒的基本使用Java執行緒
- 【Java】【多執行緒】執行緒的生命週期Java執行緒
- Java多執行緒詳解——一篇文章搞懂Java多執行緒Java執行緒
- Java多執行緒之守護執行緒實戰Java執行緒
- 深入淺出Java多執行緒(十二):執行緒池Java執行緒
- Java 多執行緒基礎(八)執行緒讓步Java執行緒
- java多執行緒:執行緒池原理、阻塞佇列Java執行緒佇列
- java多執行緒5:執行緒間的通訊Java執行緒
- Java多執行緒之執行緒同步【synchronized、Lock、volatitle】Java執行緒synchronized
- Java中的多執行緒Java執行緒