1. 程式人生 > >圖解Java多執行緒設計模式 序章1 Java執行緒

圖解Java多執行緒設計模式 序章1 Java執行緒

【Thread類的run方法和start方法】

    JAVA程式執行時,最開始執行的只能是主執行緒。所以必須在程式中啟動新執行緒。

    啟動執行緒時,要使用如下類(一般稱為Thread類)

public class MyThread extends Thread {

     public void run(){

          for(int i=0;i<100;i++){

              System.out.println("Nice!");

          }

     }

}

  新啟動的執行緒的操作都編寫在run方法中。新執行緒啟動後會呼叫run方法。隨後,當run方法執行結束時,執行緒也會跟著終止。

  使用者啟動執行緒的程式碼如下

public static void main(String[] args){

    MyThread t = new MyThread();      //主執行緒建立MyThread類的例項

    t.start();                        //由主執行緒啟動新執行緒

    for(int i=0;i<100;i++){

         System.out.println("Good!");

    }

}

start方法是Thread類中的方法,用於啟動新的執行緒。

呼叫start方法後,程式會在後臺啟動新的執行緒。然後,由這個新執行緒呼叫run方法。

【順序、並行與併發】

順序用於表示多個操作 “依次處理”。比如把十個操作交給一個人處理時,這個人要一個一個地按順序來處理。

並行用於表示多個操作“同時處理”。比如十個操作分給兩個人處理時,這兩個人就會並行來處理。

併發相對於順序和並行來說比較抽象,用於表示“將一個操作分割成多個部分並且允許無序處理”。比如將十個操作分成相對獨立的兩類,這樣便能夠哦開始併發處理了。

【執行緒的啟動】

啟動執行緒的方法有如下兩種:

    1    利用Thread類的子類的例項啟動執行緒

    2    利用Runnable介面的實現類的例項啟動執行緒

執行緒的啟動一:利用Thread類的子類

    new MyThread().start();

執行緒的啟動二:利用Runnable介面

    Runnable介面包含在java.lang包中,宣告如下

public interface Runnable {

    public abstract void run();

}

Runnable介面的實現類必須要實現run方法。

建立Runnable介面的實現類。將實現類的例項作為引數傳給Thread的建構函式,呼叫start方法。

  new Thread(new Printer("Good")).start();

(小知識java.util.concurrent.ThreadFactory中的執行緒建立)

ThreadFactory就是一個執行緒工廠也就是負責生產執行緒的。java.util.concurrent中包含一個將執行緒建立抽象化的ThreadFactory介面。利用該介面,我們可以將以Runnable作為傳入引數並通過new建立Thread例項的處理隱藏在ThreadFactory內部。

預設的ThreadFactory物件是通過Executors.defaultThreadFactory方法獲取的。

public static void main(String[] args){

     ThreadFactory factory = Executors.defaultThreadFactory();

     factory.newThread(new Printer()).start();

     for(int i=0;i<100;i++){

          System.out.println("hello!");

     }

}

【執行緒的暫停】

執行緒Thread類中的sleep方法能夠暫停執行緒執行,sleep是Thread類的靜態方法。

public static void main(String[] args){

     for(int i=0;i<100;i++){

          System.out.println("hello!");

          try{

              Thread.sleep(1000);

          }catch(InterruptedException e){

          }

     }

}

【執行緒的互斥處理】

多執行緒程式的各個執行緒都是自由執行的,所以他們有時就會同時操作同一個例項。這在某些情況下會引發問題。

java使用關鍵字synchronized來執行執行緒的互斥處理。

【synchronized方法】

如果宣告一個方法是,在前面加上關鍵字synchronized,那麼這個方法就 只能由一個執行緒執行。稱為同步方法。

public class Bank {

     private int money;

     private String name;

     

     public Bank(String name, int money){

          this.name = name;

          this.money = money;

     }

     //存款

     public synchronized void deposit(int m){

          money+=m;

     }

     //取款

     public synchronized boolean withdraw(int m){

          if(money>=m) {

              money-=m;

              return true;//取款成功

          } else {

              return false;//餘額不足

          }

     }

     public String getName(){

          return name;

     }

}

【執行緒的協作】

如果有一個執行緒正在執行synchronized方法,那麼其他執行緒就無法再執行這個方法了。這是簡單的互斥處理。

假如我們現在想執行更加精確的控制,而不是單純地等待其他執行緒執行終止,例如下面這樣的控制:

    如果空間為空則寫入資料;如果非空則一直等待到變空為止

    空間已為空時,通知正在等待的執行緒。

Java提供了用於執行執行緒控制的wait方法、notify方法和notifyAll方法。wait是讓執行緒等待的方法,而notify和notifyAll是喚醒等待中的執行緒的方法。

【等待佇列——執行緒休息室】

所有例項都擁有一個等待佇列,它是在例項的wait方法執行後停止操作的執行緒的佇列。

在執行wait方法後,執行緒便會暫停操作,進入等待佇列這個休息室。除非發生下列某一情況,否則執行緒會一直在等待佇列中休眠:

    1.有其他執行緒的notify方法喚醒執行緒

    2.有其他執行緒的notifyAll方法喚醒執行緒

    3.有其他執行緒的interrupt方法喚醒執行緒

    4.wait方法超時

若要執行wait方法,執行緒必須持有鎖。但如果執行緒進入等待佇列,便會釋放其例項的鎖。

【習題1 閱讀下面內容,敘述正確請打√,錯誤請打×】

1.在Java程式中,至少有一個執行緒在執行。(√)

2.呼叫Thread類的run方法後,新的執行緒就會啟動。(×)

答:啟動新執行緒的並不是run方法,而是start方法。

3.start方法和run方法宣告在Runnable介面中。(×)

答:只有run方法宣告在Runnable介面中。

4有時多個執行緒會呼叫同一個例項的方法。(√)

答:正因為如此,我們才需要執行執行緒的互斥處理。

5.有時多個執行緒會呼叫Thread類的一個例項的方法。(√)

答:雖說是Thread類的例項方法,但與其他類的例項方法並沒有什麼不同,所以也會被多個執行緒呼叫。

6.sleep方法執行後,在指定的時間所有的執行緒都會暫停。(×)

答:暫停的只是呼叫了sleep方法的執行緒。

7.某個執行緒在執行synchronized方法時,其他所有執行緒都會停止執行。(×)

答:停止執行的只是想要獲取同一個例項的鎖的執行緒。

8.執行sleep方法後的執行緒僅在指定時間內待在等待佇列中。(×)

答:執行sleep方法後的執行緒並不會進入等待佇列。只有在wait方法後,執行緒才會進入等待佇列。

9.wait方法的呼叫語句必須寫在synchronized方法中。(×)

答:synchronized方法或synchronized程式碼塊。只要執行wait方法的執行緒在執行時獲得了物件例項的鎖即可。

10.notifyAll方法是java.lang.Object類的例項方法。(√)

【習題2】

當下面的程式執行時,程式會在輸出1000個“*”後,再輸出1000個“+”。請問,為什麼輸出結果並不是“*”和“+”交錯混雜的呢?

public class main{

    public static void main(String[] args){

        new PrintThread("*").run();

        new PrintThread("+").run();

    }

}

public class printThread extends Thread{

    private String message;

    public printThread(String message){

        this.message=message;

    }

    public void run(){

        for(int i=0;i<1000;i++){

            System.out.print(message);

        }

    }

}

答:因為主執行緒呼叫的不是start方法,而是run方法。new PrintThread("*").run();這條語句會建立PrintThread類的例項,並執行該例項的run方法。但執行run方法的並不是新執行緒,而是主執行緒。當輸出100個“*”之後,下面的語句才會被執行。new PrintThread("+").run();

最終,所有的輸出都是由主執行緒這一個執行緒來執行的,也就是說,這個程式其實是一個單執行緒程式。

//建立例項

MyThread t=new MyThread();

//啟動執行緒

t.start();

//暫停已啟動的執行緒

try{

    t.sleep(1000);

}catch(InterruptedException e){

}

【習題3】

某人寫了如下程式碼,想讓啟動的執行緒暫停約1秒。但這個程式碼是錯誤的,為什麼呢?假設下面的MyThread就是程式碼清單I1-2中宣告的那個類。

答:這是因為執行t.sleep(1000);時暫停的並不是與t相關的那個執行緒,而是執行這條語句的執行緒。

t.sleep(1000);

上面這條語句調用出來的並不是t的例項方法,而是Thread的靜態方法。也就是說,這等同於執行下面這條語句。

Thread.sleep(1000);

當想要暫停新啟動的執行緒時,我們可以在MyThread類的run方法中呼叫sleep方法。

【習題4】

假設存在一個如下宣告的Something類,變數x,y表示Something類的兩個不同例項。請判斷下列組合是否允許多個執行緒同時執行,允許請畫√,否則請畫×。

public classSomething {

    public void iA(){ }

    public void iB(){ }

    public synchronized void isSyncA(){ }

    public synchronized void isSyncB(){ }

    public static void cA(){ }

    public static void cB(){ }

    public static synchronized void cSyncA(){ }

    public static synchronized void cSyncB(){ }

}

√(1)x.iA(); 與 x.iA();

答:非synchronized方法可在任意時間多個執行緒執行。

√(2)x.iA(); 與 x.iB();

答:非synchronized方法可在任意時間多個執行緒執行。

√(3)x.iA(); 與 x.iSyncA();

答:非synchronized方法可在任意時間多個執行緒執行,即使存在正在執行其他的synchronized方法的執行緒,非synchronized方法也任然可以由多個執行緒執行。

×(4)x.iSyncA(); 與 x.iSyncA();

答:同一個例項的synchronized例項方法同時只能由一個執行緒執行。

×(5)x.iSyncA(); 與 x.iSyncA();

答:同一個例項的synchronized例項方法同時只能由一個執行緒執行。

√(6)x.iSyncA(); 與 y.iSyncA();

答:例項不同,鎖也就不同,所以就算是synchronized例項方法,也可以由多個執行緒同時執行。

√(7)x.iSyncA(); 與 x.iSyncB();

答:例項不同,鎖也就不同,所以就算是synchronized例項方法,也可以由多個執行緒同時執行。

√(8)x.iSyncA(); 與 Something.cA();

答:靜態方法本來就不是synchronized方法,因此可以同時執行。

√(9)x.iSyncA(); 與 Something.cSyncA();

答:synchronized例項方法和synchronized靜態方法的鎖不同,所以可以由多個執行緒同時執行。

×(10)Something.cSyncA(); 與 Something.cSyncA();

答:synchronized靜態方法不可以有多個執行緒同時執行。

×(11)Something.cSyncA(); 與 Something.cSyncB();

答:synchronized靜態方法不可以有多個執行緒同時執行。

×(12)x.cSyncA(); 與 y.cSyncB();

答:synchronized靜態方法不可以有多個執行緒同時執行。