Threads
Outline Introduction to Threads How to create Thread extend Thread implement Runnable interface Synchronization
What is thread? 定義 : 程式的執行軌跡 Single Thread Multi-Thread 依序執行 int x, y; int z; x = 3; y = x + 4; z = 5; int x, y; x = 3; y = x + 4; int z; z = 5; CPU CPU
Program, Process, Thread 程式 (Program) 儲存於硬碟中的可執行檔稱為 Program 行程 (Process) 載入記憶體中的可執行檔稱為 Process 執行緒 (Thread) Process 中的一個程式碼執行軌跡稱為 Thread, 是電腦中最小的執行單位 Program Process Thread HDD Memory
建立一個執行緒的方法 要建立一個 Thread, 先要建造一個 Thread 物件, 再用 new 產生 Thread 的個體 您 new 幾次, 就有幾個 Thread 建造一個 Thread 物件的方法 : 直接繼承 java.lang.thread 實作 Runnable 介面 事實上 : java.lang.thread 就是一個實作 Runnable 的物件
Thread 物件簡介 與 行為 有關的方法 : void run(): 定義此執行緒的任務 一旦執行緒醒過來後,run() 裡面定義什麼, 它就執行什麼 執行完 run() 後就會死 void start(): 開始執行 Thread 此方法會觸動 run() 函數 boolean isalive(): 傳回此 Thread 是否活動中 static void yield(): 把自己暫停, 先禮讓其它相同優先順序的 Thread 執行 static void sleep(long millis): 讓自己小睡片刻, 經過 millis 微秒 (ms) 後再醒過來 void join(): 停止執行, 等死 void destroy(): 命令 Thread 馬上結束
Thread 物件簡介 與 屬性 有關的方法 : void setname(string name): 為此執行緒取個名字 String getname(): 傳回此執行緒的名字 void setpriority(int newpriority): 設定此執行緒的優先順序 數字越大, 優先順序越高 int getpriority(): 傳回此執行緒的優先順序 String tostring(): 傳回此執行緒的名稱, 優先順序, 及所屬群組 Thread 物件內的常數 static int MAX_PRIORITY: 最高優先順序值 static int MIN_PRIORITY: 最低優先順序值 static int NORM_PRIORITY: 預設優先順序值
Thread 物件簡介 七大建構元 Thread() Thread(String name) Thread(Runnable target) Thread(Runnable target, String name) Thread(ThreadGroup group, String name) Thread(ThreadGroup group, Runnable target) Thread(ThreadGroup group, Runnable target, String name);
Thread 物件簡介 建構元參數介紹 : String name: 此 Thread 的名字 如果沒有提供, 預設的名字是 Thread-n, 其中 n 是一個整數 Runnable target: 使用其它類別內的 run() 函式作為此類別的 run() 函式 ThreadGroup group: 指定此 Thread 應隸屬於哪一個 Thread Group
繼承 Thread 的範例 class Horse extends Thread { final int MAX_DISTANCE = 600; int RaceDistance = 400; int CurrentDistance = 0; long RaceTime = 0L; // Constructors public Horse() { super(); public Horse(String name) { super(name); public Horse(String name, int distance) { super(name); RaceDistance = (distance < MAX_DISTANCE)? distance: MAX_DISTANCE;
繼承 Thread 的範例 public void run() { long begin = 0L, end = 0L; begin = System.currentTimeMillis(); while (CurrentDistance <= RaceDistance) { CurrentDistance += Math.floor(Math.random() * 10); System.out.println(getName() + ": " + CurrentDistance); try { sleep((long)(math.random() * 1000)); catch (InterruptedException e) { end = System.currentTimeMillis(); RaceTime = end - begin; public int getcurrentdistance() { return CurrentDistance; public long getracetime() { return RaceTime;
繼承 Thread 的範例 public class RaceHorse { public static void main(string[] args) { Horse Rubby = new Horse("Rubby", 40); Horse Lisa = new Horse("Lisa", 40); Rubby.start(); Lisa.start(); while (Rubby.isAlive() Lisa.isAlive()) { // Do nothing, just wait for the end. if (Rubby.getRaceTime() < Lisa.getRaceTime()) System.out.println("Rubby win!! Record: " + Rubby.getRaceTime() + " ms"); else if (Rubby.getRaceTime() > Lisa.getRaceTime()) System.out.println("Lisa win!! Record: " + Lisa.getRaceTime() + " ms"); else System.out.println("Deuce! Record: " + Rubby.getRaceTime());
Runnable 介面簡介 用途 自訂 Thread 時, 除了繼承 java.lang.thread, 也可實作 Runnable 介面 使用時機 當您自訂的類別已經繼承其它類別, 卻又要變成一個 Thread 物件時 方法 : void run() Runnable 內唯一需要程式師實作的方法
實作 Runnable 介面的範例 class RunHorse extends Thread {. public void run() { long begin = 0L, end = 0L; begin = System.currentTimeMillis(); while (CurrentDistance <= RaceDistance) { CurrentDistance += Math.floor(Math.random() * 10); System.out.println(getName() + ": " + CurrentDistance); try { sleep((long)(math.random() * 1000)); catch (InterruptedException e) { end = System.currentTimeMillis(); RaceTime = end - begin;. 將原先 class Horse 中的 System.out.println 刪除, 改名為 class RunHorse 重編譯即可
實作 Runnable 介面的範例 import java.applet.*; import java.awt.*; public class RunningHorse extends Applet implements Runnable { private Thread horsethread = null; int totalhorses; int racedistance; RunHorse[] TheHorse; Image[] imghorse; AudioClip MyAudio; int thewinner; private boolean bracefinished = false; private String WinnerMsg = "";
實作 Runnable 介面的範例 public void init() { totalhorses = Integer.parseInt(getParameter("TotalHorses")); racedistance = Integer.parseInt(getParameter("RaceDistance")); horsethread = new Thread(this, "RunningHorse"); horsethread.setpriority(thread.norm_priority + 5); TheHorse = new RunHorse[totalHorses]; imghorse = new Image[totalHorses]; for (int i=0; i<totalhorses; i++) { TheHorse[i] = new RunHorse(getParameter("Horse" + i), racedistance); imghorse[i] = getimage(getdocumentbase(), "Images/Horse" + i + ".gif"); MyAudio = getaudioclip(getdocumentbase(), "Audio/RunHorse.au");
實作 Runnable 介面的範例 public void run() { boolean bsomeonealive; do { repaint(); bsomeonealive = false; for (int i=0; i<totalhorses; i++) bsomeonealive = bsomeonealive TheHorse[i].isAlive(); while (bsomeonealive); thewinner = 0; for (int i=0; i<totalhorses; i++) { if (TheHorse[theWinner].getRaceTime() > TheHorse[i].getRaceTime()) thewinner = i; WinnerMsg = "The Winner is " + TheHorse[theWinner].getName(); bracefinished = true; MyAudio.stop();
實作 Runnable 介面的範例 public boolean mousedown(event evt, int x, int y) { // When mouse click, enable all threads for (int i=0; i<totalhorses; i++) if (! TheHorse[i].isAlive()) TheHorse[i].start(); if (! horsethread.isalive()) horsethread.start(); MyAudio.loop(); return true;
實作 Runnable 介面的範例 public void paint(graphics g) { // Show the message on top if (horsethread.isalive()) g.drawstring("go! Go! Racing Horses...", 5, 25); else g.drawstring("reload the page and click to play...", 5, 25); // Draw the racing field g.setcolor(color.yellow); g.fillrect(0, 100, racedistance, totalhorses * 100);
實作 Runnable 介面的範例 // Draw the Horses, name and line int i; int ThisX, ThisY; g.setcolor(color.black); for (i=0; i<totalhorses; i++) { ThisX = TheHorse[i].getCurrentDistance(); ThisY = (i+1)*100; g.drawimage(imghorse[i], ThisX, ThisY, this); g.drawline(0, ThisY+50, ThisX, ThisY+50); g.drawstring(thehorse[i].getname(), racedistance+110, ThisY+50);
實作 Runnable 介面的範例 if (bracefinished) { g.drawstring(winnermsg, 5, 95); for (i=0; i<totalhorses; i++) { g.drawstring(string.valueof(thehorse[i].getracetime()), racedistance+110, (i+1)*100+75);
執行緒同步問題 所謂 執行緒同步問題 是指兩個執行緒同時存取一個共用變數時, 所會發生的問題 : 王先生 $10000 王太太 $10000 $10000 - $1000 - $9000 $9000 $1000 $1000 $9000 提領 $1000 提領 $1000 提領 $9000
執行緒同步問題 王先生 $10000 王太太 $10000 $10000 問題就出在這裡
執行緒同步問題 解決方法 :Lock Wait - Notify 王先生 $10000 王太太 $10000 Lock Wait - $1000 $9000 $9000 Notify $9000
執行緒同步問題 Java 的解決方法 : Lock: 使用 synchronized 保留字 Wait: 使用 Wait() 函數 Notify: 使用 Notify() 或 NotifyAll()
未經過同步保護的例子 public class MyBank implements Runnable { int balance = 100000; public static void main(string[] args) { MyBank bank = new MyBank(); Thread Husbon = new Thread(bank, "Mr. Wang"); Thread Wife = new Thread(bank, "Mrs. Wang"); Husbon.start(); Wife.start();
未經過同步保護的例子 public void run() { String name = Thread.currentThread().getName(); for(int i=0; i<10; i++) { balance -= 1000; System.out.println(name + ": " + balance); try { Thread.currentThread().sleep((long)(Math.random()*500)); catch(interruptedexception e) {
經過同步保護的例子 public class MyBank implements Runnable { int balance = 100000; public static void main(string[] args) { MyBank bank = new MyBank(); Thread Husbon = new Thread(bank, "Mr. Wang"); Thread Wife = new Thread(bank, "Mrs. Wang"); Husbon.start(); Wife.start();
經過同步保護的例子 public void run() { String name = Thread.currentThread().getName(); for(int i=0; i<10; i++) { synchronized (this) { balance -= 1000; // this block allow only one thread System.out.println(name + ": " + balance); try { Thread.currentThread().sleep((long)(Math.random()*500)); catch(interruptedexception e) {
另一種同步保護的寫法 public class MyBank implements Runnable { int balance = 100000; public static void main(string[] args) { MyBank bank = new MyBank(); Thread Husbon = new Thread(bank, "Mr. Wang"); Thread Wife = new Thread(bank, "Mrs. Wang"); Husbon.start(); Wife.start();
另一種同步保護的寫法 public void run() { String name = Thread.currentThread().getName(); for(int i=0; i<10; i++) { withdraw(1000); System.out.println(name + ": " + balance); try { Thread.currentThread().sleep((long)(Math.random()*500)); catch(interruptedexception e) { protected synchronized void withdraw(int money) { balance -= money;