1. 程式人生 > >《Java程式設計思想第四版》筆記---21章(1) 併發

《Java程式設計思想第四版》筆記---21章(1) 併發

執行緒是程序中一個任務控制流序列,由於程序的建立和銷燬需要銷燬大量的資源,而多個執行緒之間可以共享程序資料,因此多執行緒是併發程式設計的基礎。

多核心CPU可以真正實現多個任務並行執行,單核心CPU程式其實不是真正的並行執行,而是通過時間片切換來執行,由於時間片切換頻繁,使用者感覺程式是在並行執行。單核心CPU中通過時間片切換執行多執行緒任務時,雖然需要儲存執行緒上下文,但是由於不會被阻塞的執行緒所阻塞,因此相比單任務還是大大提高了程式執行效率。

1.執行緒的狀態和切換(P694)

執行緒的7種狀態及其切換圖如下:


2.多執行緒簡單執行緒例子

Java中實現多執行緒常用兩種方法是:實現Runnable介面和繼承Thread類。

(1).繼承Thread類的多執行緒例子如下(P655 21.2.2)

  1. classPrimeThreadextendsThread{
  2. long minPrime;
  3. PrimeThread(long minPrime){
  4. this.minPrime = minPrime;
  5. }
  6. //重寫Thread類的run方法
  7. publicvoid run(){
  8. ...
  9. }
  10. }

啟動繼承Thread類執行緒的方法:

  1. PrimeThread p =newPrimeThread(143);
  2. p.start();

(2).實現Runnable介面的多執行緒例子如下(P654 21.2.1)

  1. classPrimeRun
    implementsRunnable{
  2. long minPrime;
  3. PrimeRun(long minPrime){
  4. this.minPrime = minPrime;
  5. }
  6. publicvoid run(){
  7. ...
  8. }
  9. }

啟動實現Runnable介面執行緒的方法:

  1. PrimeThread p =newThread(newPrimeThread(143));
  2. p.start();
由於java的單繼承特性和麵向介面程式設計的原則,建議使用實現Runnable介面的方式實現java的多執行緒。

3.使用Executors執行緒池(P656 21.2.3)

在JDK5中,在java.util.concurrent包中引入了Executors執行緒池,使得建立多執行緒更加方便高效,例子如下:

  1. import java.util.concurrent.*;
  2. publicclassCachedThreadPool{
  3. publicstaticvoid main(String[] args){
  4. //建立一個緩衝執行緒池服務
  5. ExecutorService exec =Executors.newCachedThreadPool();
  6. for(int i=0; i<5; i++){
  7. //執行緒池服務啟動執行緒
  8. exec.execute(
  9. newRunnable(){
  10. //使用匿名內部類實現的java執行緒
  11. publicvoid run(){
  12. System.out.println(“Thread+ i + is running”);
  13. }
  14. }
  15. );
  16. //執行緒池服務停止
  17. exec.shoutdown();
  18. }
  19. }
  20. }

Executors.newCachedThreadPool()方法建立緩衝執行緒池,即在程式執行時建立儘可能多需要的執行緒,之後停止建立新的執行緒,轉而通過迴圈利用已經建立的執行緒。Executors.newFixedThreadPool(intsize)方法建立固定數目的執行緒池,即程式會建立指定數量的執行緒。緩衝執行緒池效率和效能高,推薦優先考慮使用。

Executors.newSingleThreadPool()建立單執行緒池,即固定數目為1的執行緒池,一般用於長時間存活的單任務,例如網路socket連線等,如果有多一個任務需要執行,則會放進佇列中順序執行。

shutdown的作用是可以防止新任務被提交到這個Executor,已經提交的任務會繼續執行,直到所有已提交任務全部結束,程式才會退出。

4.獲取執行緒的返回值(P658 21.2.4)

實現Runnable介面的執行緒沒有返回值,如果想獲取執行緒的返回值,需要實現Callable介面,Callable是JDK5中引入的現實執行緒的介面,其call()方法代替Runnable介面的run方法,可以獲取執行緒的返回值,例子如下:

  1. import java.util.concurrent.*;
  2. import java.util.*;
  3. classTaskWithResultimplementsCallable<String>{
  4. privateint id;
  5. publicTaskWithResult(int id){
  6. this.id = id;
  7. }
  8. publicString call(){
  9. returnresult of TaskWithResult+ id;
  10. }
  11. publicstaticvoid main(String[] args){
  12. ExecutorService exec =Executors.newCachedThreadPool();
  13. List<Future<String>> results =newArrayList<Future<String>>();
  14. for(int i =0; i <5; i++){
  15. // 將執行緒返回值新增到List中
  16. results.add(exec.submit(newTaskWithResult(i)));
  17. }
  18. // 遍歷獲取執行緒返回值
  19. for(Future<String> fs : results){
  20. try{
  21. System.out.println(fs.get());
  22. }catch(Exception e){
  23. System.out.println(e);
  24. }finally{
  25. exec.shutdown();
  26. }
  27. }
  28. }
  29. }

輸出結果(可能的結果,由於多執行緒執行順序不確定,結果不固定):

result of TaskWithResult 0

result of TaskWithResult 1

result of TaskWithResult 3

result of TaskWithResult 4

result of TaskWithResult 5

註解:使用執行緒池服務的submit()方法執行執行緒池時,會產生Future<T>物件,其引數型別是執行緒Callable的call()方法返回值的型別,使用Future物件的get()方法可以獲取執行緒返回值。

5.執行緒休眠(P659 21.2.5)

在jdk5之前,使用Thread.sleep(1000)方法可以使執行緒休眠1秒鐘,在jdk5之後,使用下面的方法使執行緒休眠:

TimeUnit.SECONDS.sleep(1);

執行緒休眠的方法是TimeUnit列舉型別中的方法。

注意:不論是Thread.sleep還是TimeUnit的sleep執行緒休眠方法,都要捕獲InterruptedExecutors。

6.執行緒優先順序(P660 21.2.6)

執行緒的優先順序是指執行緒被執行緒排程器排程執行的優先順序順序,優先順序越高表示獲取CPU允許時間的概率越大,但是並不是絕對的,因為執行緒排程器排程執行緒是不可控制的,只是一個可能性的問題。可以通過Thread執行緒物件的getPriority()方法獲取執行緒的優先順序,可以通過執行緒物件的setPriority()方法設定執行緒的優先順序。

Java的執行緒優先順序總共有10級,最低優先順序為1,最高為10,Windows的執行緒優先順序總共有7級並且不固定,而Sun的Soloaris作業系統有23級,因此java的執行緒優先順序無法很好地和作業系統執行緒優先順序對映,所有一般只使用MAX_PRIORITY(10),NORM_PRIORITY(5)和MIN_PRIORITY(1)這三個執行緒優先順序

通過yield()或setPriority()來給執行緒排程器提供建議,但未必有多大效果,這取決於具體的平臺和JVM實現。

7.守護執行緒(P662 21.2.8)

守護執行緒(DaemonThread)是某些提供通用服務的在後臺執行的程式,是優先順序最低的執行緒。當所有的非守護執行緒執行結束後,程式會結束所有的守護執行緒而終止執行。如果當前還有非守護執行緒的執行緒在執行,則程式不會終止,而是等待其執行完成。守護程序的例子如下:

  1. import java.util.concurrent.*;
  2. publicclassSimpleDaemonsimplementsRunnable{
  3. publicvoid run{
  4. try{
  5. System.out.println(“Start daemons”);
  6. TimeUtil.SECONDS.sleep(1);
  7. }catch(Exception e){
  8. System.out.println(“sleep() interrupted”);
  9. }finally{
  10. System.out.println(“Finally is running”);
  11. }
  12. }
  13. publicstaticvoid main(String[] args)throwsException{
  14. Thread daemon =newThread(newSimpleDaemons());
  15. daemon.setDaemon(true);
  16. daemon.start();
  17. }
  18. }

輸出結果:

Start daemons

Finally沒有執行,如果註釋掉daemon.setDaemon(true)設定守護程序這一句程式碼。

輸出結果:

Start daemons

Finally is running

之所以產生這樣的結果原因是,main()是這個程式中唯一的非守護執行緒,當沒有非守護執行緒在執行時,JVM強制推出終止守護執行緒的執行。

通過Thread物件的setDaemon方法可以設定執行緒是否為守護執行緒,通過isDaemon方法可以判斷執行緒物件是否為守護執行緒。

由守護執行緒建立的執行緒物件不論有沒有通過setDaemon方法顯式設定,都是守護執行緒。

8.synchronized執行緒同步:

程式設計中的共享資源問題會引起多執行緒的競爭,為了確保同一時刻只有一個執行緒獨佔共享資源,需要使用執行緒同步機制,即使用前對共享資源加鎖,使用完畢之後釋放鎖。

Java中通過synchronized關鍵字實現多執行緒的同步,執行緒同步可以分為以下幾種:

(1).物件方法同步:

  1. publicsynchronizedvoid methodA(){
  2. System.out.println(this);
  3. }

每個物件有一個執行緒同步鎖與之關聯,同一個物件的不同執行緒在同一時刻只能有一個執行緒呼叫methodA方法。

(2).類所有物件方法同步:

  1. publicsynchronizedstaticvoid methodB(){
  2. System.out.println(this);
  3. }

靜態方法的執行緒同步鎖對類的所有物件都起作用,即所有物件的執行緒在同一時刻只能有一個類的一個執行緒呼叫該方法。

(3).物件同步程式碼塊(P689 21.3.6)

  1. publicvoid methodC(){
  2. synchronized(this){
  3. System.out.println(this);
  4. }
  5. }

使用當前物件作為執行緒同步鎖,同一個物件的不同執行緒在同一時刻只能有一個執行緒呼叫methodC方法中的程式碼塊。

(4).類同步程式碼塊:

  1. publicvoid methodD(){
  2. synchronized(className.class){
  3. System.out.println(this);
  4. }
  5. }

使用類位元組碼物件作為執行緒同步鎖,類所有物件的所有執行緒在同一時刻只能有一個類的一個執行緒呼叫methodD的同步程式碼塊。

注意:執行緒的同步是針對物件的,不論是同步方法還是同步程式碼塊,都鎖定的是物件,而非方法或程式碼塊本身。每個物件只能有一個執行緒同步鎖與之關聯。

如果一個物件有多個執行緒同步方法,只要一個執行緒訪問了其中的一個執行緒同步方法,其它執行緒就不能同時訪問這個物件中任何一個執行緒同步方法。

同步程式碼塊跟方法同步的區別是前者可以使多個任務訪問物件的時間效能得到顯著提高。

9.執行緒鎖(P678)

JDK5之後,在java.util.concurrent.locks包中引入了執行緒鎖機制,程式設計中可以顯式鎖定確保執行緒間同步,例子如下:

  1. import java.util.concurrent.*;
  2. import java.util.concurrent.locks.*;
  3. publicclassLocking{
  4. boolean captured =false;
  5. try{
  6. //物件鎖定兩秒鐘
  7. captured = lock.tryLock(2,TimeUnit.SECONDS);
  8. }catch(InterruptedException e){
  9. ThrownewRuntimeException(e);
  10. }try{
  11. System.out.println(“tryLock(2,TimeUnit.SECONDS):+ captured);
  12. }finally{
  13. if(captured){
  14. 相關推薦

    Java程式設計思想筆記---211 併發

    執行緒是程序中一個任務控制流序列,由於程序的建立和銷燬需要銷燬大量的資源,而多個執行緒之間可以共享程序資料,因此多執行緒是併發程式設計的基礎。 多核心CPU可以真正實現多個任務並行執行,單核心CPU程式其實不是真正的並行執行,而是通過時間片切換來執行,由於時間片切換頻繁,使

    Java程式設計思想 手碼原書+菜鳥筆記 第一 1.12 併發程式設計

    文中筆記均為個人觀點,如有錯誤請大家不吝指出,謝謝! 原書為《java程式設計思想 第四版 中文版》 在計算機程式設計中有一個基本概念,就是在同一時刻處理多個任務的思想。 許多程式設計問題都要求,程式能夠停下正在做的工作,轉而處理某個其

    Java程式設計思想讀書筆記—— 初始化與清理

    第五章 初始化與清理 1 用構造器確保初始化 使用構造器(condructor),在建立物件時初始化。分為帶引數的初始化和不帶引數的初始化。 2 方法過載 型別提升(向上提升):int — long — float — double    

    Java程式設計思想讀書筆記—— 介面

    這章介紹了介面卡設計模式和策略設計模式。 第九章  介面 介面和內部類為我們提供了一種將介面與實現分離的更加結構化的方法。 1、抽象類和抽象方法 public abstract void f(); 建立抽象類是希望通過這個通用介面操縱一系列類。如果一個類

    Java程式設計思想 手碼原書+菜鳥筆記 第二 2.1 用引用操縱物件

    上一篇: 第二章 一切都是物件 文中筆記均為個人觀點,如有錯誤請大家不吝指出,謝謝! 原書為《java程式設計思想 第四版 中文版》 每種程式語言都有自己的操縱記憶體中元素的方式。有時候,程式設計師必須注意將要處理的資料是什麼型別。你

    Java程式設計思想 手碼原書+菜鳥筆記 第二 一切都是物件

    上一篇: 1.14 總結 文中筆記均為個人觀點,如有錯誤請大家不吝指出,謝謝! 原書為《java程式設計思想 第四版 中文版》 “如果我們說另一種不同的語言,那麼我們就會發覺一個有些不同的世界。” ——Luduing Wittgers

    Java程式設計思想-控制執行流程 筆記

                                                   第四章 控制執行流程 有一定java基礎的其實這一章節都懂的。。。。主要記一些細節。(斷句,和switch的 用法。) if- else  while    do-while  

    Java程式設計思想讀書筆記——十二 通過異常處理錯誤

    第十二章 通過異常處理錯誤 Java的基本理念是“結構不佳的程式碼不能執行”。 Java中異常處理的目的在於通過使用少於目前數量的程式碼來簡化大型、可靠的程式的生成,並且通過這種方式可以使程式設計師增加自信。 1、概念 因為異常機制將保證能夠捕獲這個錯誤,所以不用小心翼翼

    Java程式設計思想讀書筆記—— 控制執行流程

    Java程式設計思想第四版第四章讀書筆記——控制執行流程。因為有些C++和android開發基礎,所以基礎知識筆記就不寫了,記些特別的需要注意的地方。 第四章 控制執行流程 1.true和false Java不允許我們將一個數字作為布林值使用,雖然這在C和C++裡是

    閱讀筆記--java程式設計思想 --介面巢狀

    /** * 該類是用於說明巢狀介面 */ class A{ interface B{ void fun(); } public class BImp implements B{ public void

    java程式設計思想學習筆記-內部類

    1、什麼是內部類? 就是在一個類的內部定義的一個類 2、為什麼要使用內部類? (1)內部類可以進行更好的封裝,對於包中的其他類來說,內部類可以被隱藏起來。 (2)可以通過繼承和介面實現多繼承。 (3)使用匿名內部類可以更好的實現回撥函式 (4)內部類可以非常方便的編寫驅動程

    Thinking in Java 4thJava程式設計思想文件、原始碼、習題答案

      Thinking in Java 4th 中、英文兩版pdf文件,書中原始碼及課後習題答案。連結:https://pan.baidu.com/s/1BKJdtgJ3s-_rN1OB4rpLTQ 密碼:2zc4   http://greggordon.org/java/tij4/solutions.

    最新最簡的改版的 java程式設計思想中net.mindview.util包下載,及原始碼簡單匯入使用

    在學習  《java程式設計思想》的時候當時苦於無法使用    import static net.mindview.util.Print.*;   用   print  做輸出,就查了網上別人的做法,後來按照togee

    java程式設計思想中net.mindview.util包下載,及原始碼簡單匯入使用

    今日我發現不能開啟  《java程式設計思想》一書所提供的網站:www.MindView.net 。所以我就找了一下別人的推薦,還是可以的,所以就分享給大家! net.mindview.util包:百度網盤:點選下載  密碼: ggpi   java程式設計思想第四版原始

    《ThinkinginJavathEditionJAVA程式設計思想 英文版》pdf附網盤下載連結+附一個菜鳥的java學習之路

    技術書閱讀方法論 一.速讀一遍(最好在1~2天內完成) 人的大腦記憶力有限,在一天內快速看完一本書會在大腦裡留下深刻印象,對於之後複習以及總結都會有特別好的作用。 對於每一章的知識,先閱讀標題,弄懂大概講的是什麼主題,再去快速看一遍,不懂也沒有關係,但是一定要在不懂的

    Java程式設計思想 5.9 列舉

    列舉: 在數學和電腦科學理論中,一個集的列舉是列出某些有窮序列集的所有成員的程式,或者是一種特定型別物件的計數。這兩種型別經常(但不總是)重疊。 [1] 是一個被命名的整型常數的集合,列舉在日常生活中很常見,例如:表示星期的SUNDAY、MONDAY、TUESD

    Java-Java程式設計思想 十五 練習

    練習1:// Use Holder3 with the typeinfo.pets library to show that a Holder3 that is // specified to hold a base type can also hold a derived

    Java程式設計思想練習

    練習1:在某個包中建立一個類,在這個類所處的包的外部建立該類的一個例項。 package exercise6; import Exercise5.Exercise5_22; public cla

    Java-Java程式設計思想 練習

    練習1:/* Write a class named Outer that contains an inner class named Innet. * Add a method to Outer that returns an object of type Inner.

    Java-Java程式設計思想 十一 練習

    練習1:/* Create a new class called Gerbil with an int gerbilNumber that's * initialized in the constructor. Give it a method called hop() t