740 likes | 868 Views
課程參與度之評估方式. 上課時必須專心聽講,跟上進度,參與討論 扣 分項目 玩線上遊戲一次扣 1 分 玩手機一次扣 1 分 睡覺一次扣 1 分 聊天一次扣 1 分 無法回答老師提出的問題一次扣 1 分 加分項目 主動回答老師的問題一次加 2 分 找出老師程式中的錯誤一次加 1 分 修正老師程式中的錯誤一次加 4 分. 網路程式設計 第七章 多執行緒程式設計. 鄧姚文. 大綱. 執行緒 繼承 Thread 類別 實作 Runnable 介面 三匹馬的賽事 管理執行緒 伺服器的多執行緒. 前言. 多執行緒程式設計 多個執行緒可以模擬出平行處理的效果
E N D
課程參與度之評估方式 • 上課時必須專心聽講,跟上進度,參與討論 • 扣分項目 • 玩線上遊戲一次扣1分 • 玩手機一次扣1分 • 睡覺一次扣1分 • 聊天一次扣1分 • 無法回答老師提出的問題一次扣1分 • 加分項目 • 主動回答老師的問題一次加2分 • 找出老師程式中的錯誤一次加1分 • 修正老師程式中的錯誤一次加4分
大綱 執行緒 繼承Thread類別 實作Runnable介面 三匹馬的賽事 管理執行緒 伺服器的多執行緒
前言 • 多執行緒程式設計 • 多個執行緒可以模擬出平行處理的效果 • 將一個CPU的執行時間切割為很小的單位,將這些單位分給多個行程去使用 • 模擬出多工(multi-task)的效果
只有一個CPU實體 分時多工: 三個行程同時執行
前言 • main執行緒 • 一個可以執行的Java類別,在main這個特別的方法被執行時,就建立了一個執行緒 • main方法的開始到結束 • 若設計一個賽馬的程式,就只能讓一匹馬從起點跑到終點
7-1 執行緒 • 執行緒的四種狀態 • 執行中(Running) • 正在執行中 • 暫停(Suspended) • 暫時停止執行,稍後可以繼續執行(Resume) • 被阻擋(Blocked,卡住?) • 因所需資源被其他執行緒占用,進入等待狀態 • 終止(Terminated) • 停止執行,無法再繼續執行
7-1 執行緒 • Thread類別的方法 • java.lang.Thread讓設計者向作業系統取得執行緒 • 以此包裝待執行的工作 • 繼承 Thread 類別即可讓新類別具備執行緒功能
7-1 執行緒 • main方法也是個執行緒 • 每個Java應用程式都至少擁有一個執行緒 • currentThread() 可取得目前執行緒物件
7-1 執行緒 01 package com.ch07; 02 03 public class ThreadName { 04 public static void main(String[] args) { 05 Thread thr = Thread.currentThread(); 06 System.out.println("目前執行緒名稱:"+thr.getName()); 07 thr.setName("DEMO"); 08 System.out.println("更改後的名稱:"+thr.getName()); 09 } 10 } ▌執行結果 目前執行緒名稱:main 更改後的名稱:DEMO
7-1 執行緒 • 兩匹馬賽跑 • 若只使用main方法來設計賽馬的程式,不能達到需求 • 利用for迴圈讓兩匹馬(整數h1與h2)從0加到5000,看那匹馬先到終點(5000)
兩匹馬賽跑 • 03 public class RacingNG { • 04 public static void main(String[] args) { • 05 int h1 = 0; • 06 int h2 = 0; • 07 for (inti=0; i<5000; i++){ • 08 h1++; • 09 h2++; • 10 System.out.println("H1:"+h1); • 11 System.out.println("H2:"+h2); • 12 } • 13 } • 14 }
兩匹馬假賽跑 • 執行結果,每一次執行一定是h1先到達終點 • (省略) • H1:4996 • H2:4996 • H1:4997 • H2:4997 • H1:4998 • H2:4998 • H1:4999 • H2:4999 • H1:5000 • H2:5000
7-1 執行緒 • 兩匹馬假賽跑 • 每次執行結果都一樣,不符合實際需求 • 應該把h2的工作(從0加到5000)「搬到」另一個執行緒去處理 • 讓main執行緒與新執行緒一起執行
7-2 繼承Thread類別 新增一個Horse類別並繼承Thread 覆寫Thread的run()方法 將要執行的工作程式碼放在run()方法內
7-2 繼承Thread類別 • 產生執行緒並執行 • 將Horse類別建構出來後,再呼叫其方法start() ,即可啟動這個執行緒
跑馬執行緒程式碼 01 package com.ch07; 02 03 public class Horse extends Thread{ 04 //覆寫Thread方法run() 05 public void run(){ 06 //由1跑到5000 07 int h = 0; 08 for (inti=0; i<5000; i++){ 09 h++; 10 System.out.println(getName()+":"+h); 11 } 12 } 13 }
7-2 繼承Thread類別 • 產生執行緒並執行 • 因此,讀者可修改原程式Racing類別,在未開始執行迴圈之前,生出Horse類別的物件,再啟動這個執行緒:
跑馬主程式 01 package com.ch07; 02 03 public class Racing { 04 public static void main(String[] args) { 05 int h1 = 0; 06 //產生Horse物件並啟動執行緒 07 Horse h2 = new Horse(); 08 h2.start(); 09 for (inti=0; i<5000; i++){ 10 h1++; 11 System.out.println("H1:"+h1); 12 } 13 } 14 }
7-2 繼承Thread類別 • 產生執行緒並執行 • 程式碼與執行緒之間的示意圖如下:
7-2 繼承Thread類別 (省略) H1:4995 H1:4996 H1:4997 H1:4998 H1:4999 H1:5000 Thread-0:1496 Thread-0:1497 Thread-0:1498 Thread-0:1499 (省略) • 多次執行結果不同 • h2執行緒比main還早執行完成
7-2 繼承Thread類別 (省略) Thread-0:4998 Thread-0:4999 Thread-0:5000 H1:1053 H1:1054 H1:1055 H1:1056 H1:1057 (省略) • 多次執行結果不同 • main執行緒較h2早執行完成
7-3 實作Runnable介面 JAVA 限制單一繼承,如果自訂類別已經有繼承對象,便無法再繼承 Thread 藉由「實作Runnable介面」建立執行緒
7-3 實作Runnable介面 • implementsRunnable • 實作 run()方法 • 上節的Horse改用介面實作,新類別為HorseRunnable
7-3 實作Runnable介面 01 package com.ch07; 02 03 public class HorseRunnable implements Runnable { 04 public void run() { 05 int h = 0; 06 for (inti=0; i<5000; i++){ 07 h++; 08 System.out.println(h); 09 } 10 } 11 } implementsRunnable
7-3 實作Runnable介面 • 啟動Runnable介面執行緒 • 產生執行緒類別的物件 • HorseRunnable h3 = new HorseRunnable(); • 利用Thread類別建構子中的其中一個可接收參數Runnable物件的建構子 • Thread thr = new Thread( h3 ); • 呼叫Thread物件的start()方法 • thr.start();
7-4 三匹馬的賽事 讓main方法專心處理產生執行緒與計算賽馬名次等工作 三匹馬都以Horse執行緒來執行 在main方法中產生三個Horse執行緒物件
7-4 三匹馬的賽事 01 package com.ch07; 02 03 public class Racing3 { 04 public static void main(String[] args) { 05 Horse h1 = new Horse(); 06 Horse h2 = new Horse(); 07 Horse h3 = new Horse(); 08 h1.setName("h1"); 09 h2.setName("h2"); 10 h3.setName("h3"); 11 h1.start(); 12 h2.start(); 13 h3.start(); 14 System.out.println("main執行緒結束"); 15 } 16 }
7-4 三匹馬的賽事 • sleep方法 • sleep(long millis) • sleep方法使用時必須以try...catch處理被意外中斷的例外InterruptedException
三匹馬的賽事 01 package com.ch07; 02 03 public class Horse extends Thread { 04 // 覆寫Thread方法run() 05 public void run() { 06 try { 07 sleep(2000); 08 System.out.println("到達終點"); 09 } catch (InterruptedException e) { 10 System.out.println("被中斷了"); 11 } 12 } 13 }
7-4 三匹馬的賽事 ▌執行Racing3類別的結果如下(每次結果有可能不一樣) main執行緒結束 h1到達終點 h3到達終點 h2到達終點 • sleep方法 • 可以讓main等待三個Horse執行緒都完成後,才結束嗎?
7-4 三匹馬的賽事 h2到達終點 h3到達終點 h1到達終點 main執行緒結束 • join()方法-等待 • Thread類別的方法join(),可以用來等待該執行緒完成
以 join 讓 main 等待執行緒結束 01 package com.ch07; 02 03 public class Racing3_2 { 04 public static void main(String[] args) { 05 Horse h1 = new Horse(); 06 Horse h2 = new Horse(); 07 Horse h3 = new Horse(); 08 h1.setName("h1"); 09 h2.setName("h2"); 10 h3.setName("h3"); 11 h1.start(); 12 h2.start(); 13 h3.start(); 14 try {
以 join 讓 main 等待執行緒結束 15 h1.join(); 16 h2.join(); 17 h3.join(); 18 } catch (InterruptedException e) { 19 System.out.println("執行緒被中斷"); 20 } 21 System.out.println("main執行緒結束"); 22 } 23 }
結算賽馬的名次- Racing4 01 package com.ch07; 02 03 import java.util.Vector; 04 05 public class Racing4 { 06 public static void main(String[] args) { 07 Vector<RankHorse> rank = new Vector<RankHorse>(); 08 RankHorse h1 = new RankHorse(rank); 09 RankHorse h2 = new RankHorse(rank); 10 RankHorse h3 = new RankHorse(rank); 11 h1.setName("h1"); 12 h2.setName("h2"); 13 h3.setName("h3"); 14 h1.start(); 15 h2.start(); 16 h3.start(); 17 try { 18 h1.join();
結算賽馬的名次- Racing4 19 h2.join(); 20 h3.join(); 21 } catch (InterruptedException e) { 22 System.out.println("執行緒被中斷"); 23 } 24 System.out.println(rank); 25 System.out.println("main執行緒結束"); 26 } 27 }
01 package com.ch07; 02 import java.util.Vector; 03 04 public class RankHorse extends Thread { 05 Vector<RankHorse> rank ; 06 07 public RankHorse(Vector<RankHorse> rank){ 08 this.rank = rank; 09 } 10 11 //覆寫Thread方法run() 12 public void run(){ 13 try { 14 sleep(2000); 15 System.out.println(getName()+"到達終點"); 16 //放進rank集合中 17 rank.add(this); 18 } catch (InterruptedException e) { 19 System.out.println(getName()+"被中斷了"); 20 } 21 } 22 }
7-4 三匹馬的賽事 h1到達終點 h3到達終點 h2到達終點 [Thread[h1,5,], Thread[h3,5,], Thread[h2,5,]] main執行緒結束 • Racing4類別的執行結果如下,可以看到rank集合內依照到達順序的三匹馬:
7-5 管理執行緒 • 優先權值(Priority) • 為執行緒訂定「優先權值(Priority)」,優先權值較高的執行緒會較快進入執行的階段 • 執行緒的優先權值可以利用setPriority方法改變,亦可使用getPriority方法取得執行緒目前的優先權值。 • Thread類別中定義了MAX_PRIORITY、NORM_PRIORITY與MIN_PRIORITY整數常數,代表了最高、普通、最低優先權值
優先權值(Priority) 01 package com.ch07; 02 03 public class PriorityTester { 04 public static void main(String[] args) { 05 Thread thr = new Thread(); 06 //印出thr目前的優先權值 07 System.out.println("thr目前的優先權值:"+thr.getPriority()); 08 //變更為最高優先權 09 thr.setPriority(Thread.MAX_PRIORITY); 10 System.out.println("thr目前的優先權值:"+thr.getPriority()); 11 //變更優先權值為8 12 thr.setPriority(8); 13 System.out.println("thr目前的優先權值:"+thr.getPriority()); 14 } 15 } ▌執行結果 thr目前的優先權值:5 thr目前的優先權值:8
7-5 管理執行緒 • 執行緒的對列(queue) • 執行緒的sleep()或yeild()方法被呼叫時,會暫停目前的工作,並進入執行緒的隊列(ready queue)排隊等待下一次CPU的執行單位
7-5 管理執行緒 • public synchronizedvoid thunder(){ • //程式碼 • } • 資源的鎖定 • 避免多個執行緒存取同一個資源 • Java語言利用「同步方法(method-level)」與「同步區塊(block-level)」這兩個方式 • 同步方法 • 為一個類別的方法加上synchronized修飾字 • 限定在同一時間,只能由此一個物件使用這個方法
7-5 管理執行緒 • synchronized( 物件 ){ • 程式碼 • 程式碼 • } • 資源的鎖定 • 同步區塊 • 限定只能由單一執行緒能執行某段程式碼 • 必需指定一個需要同步的物件
7-6 伺服器的多執行緒 以main執行緒為每個客戶端程式產生執行緒,以處理個別的需求。 設計一個執行緒,可分別處理每個客戶端的需求
7-6 伺服器的多執行緒 01 package com.ch07; 02 03 import java.io.IOException; 04 import java.net.ServerSocket; 05 import java.net.Socket; 06 07 public class EchoServer { 08 public static void main(String[] args) { 09 try { 10 ServerSocket server = new ServerSocket(3333); 11 Socket socket = server.accept(); 12 } catch (IOException e) { 13 e.printStackTrace(); 14 System.out.println("伺服器窗口發生錯誤"); 15 } 16 } 17 } 多執行緒伺服器的設計
7-6 伺服器的多執行緒 01 package com.ch07; 02 03 public class EchoThread extends Thread { 04 public void run() { 05 06 } 07 } • 設計EchoThread執行緒 • 初期設計
7-6 伺服器的多執行緒 • 設計EchoThread執行緒 • 設計一建構子,接收java.net.Socket
01 package com.ch07; 02 03 import java.io.BufferedReader; 04 import java.io.IOException; 05 import java.io.InputStreamReader; 06 import java.io.OutputStreamWriter; 07 import java.io.PrintWriter; 08 import java.net.Socket; 09 10 public class EchoThread extends Thread { 11 BufferedReader in ; 12 PrintWriter out ; 13 public EchoThread(Socket ss) throws IOException{ 14 in = new BufferedReader(new 15 InputStreamReader(ss.getInputStream())); 16 out = new PrintWriter( 17 new OutputStreamWriter(ss.getOutputStream())); 18 } 19 public void run() { 20 21 } 22 }