Download presentation
Presentation is loading. Please wait.
1
Multithread 多執行緒 I/O Lecturer:曾學文
2
Important Features of Java
何謂多工 (Multi-tasking) 例如,撰寫網路程式,模擬程式。 Java 利用 “執行緒” (Thread) 來實作多工 可將一個Process分成數份, 讓各部份能同時被執行。
3
Process and Thread Process - 正在執行的應用程式 不同的行程會對應到不同的
系統資源 CPU時間 Multithread的概念特別針對CPU時間的分配 暫時閒置的CPU時間,用在執行應用程式的其他工作上 語法不難,難在掌控 執行先後順序 資源維護
4
執行緒的生命週期 Blocked start() Runnable Running yield()
notify(), I/O unblock sleep(), wait(), , I/O block start() Runnable Running yield()
5
設計背景 雖然說一次可以處理多個執行緒 不過在同一個時間點真正執行的還是只有一個 (例外:多CPU系統可以執行多個) 處理多個執行緒時
一次只有一個執行緒在執行(Running) 其他執行緒在Runnable區塊中等待 執行先後順序會依照執行緒的優先權來判定 沒執行的在Runnable區塊中等待下一次優先權的判定
6
Thread類別 java.lang.Thread 提供的基本方法
public class Thread extends Object implements Runnable 提供的基本方法 static yield() 讓目前running的暫停, 讓runnable的擇一跑 static sleep() 讓目前running的睡一個設定的時間 start() 啟動, 之後JVM可啟動該thread的run() setPriority() setPriority(MAX_PRIORITY):給最大優先權 setPriority(MIN_PRIORITY):給最小優先權 setPriority(NORM_PRIORITY):預設的優先權
7
簡單程式範例 (MyThread.java)
public class MyThread extends Thread{ MyThread(String n) { super(n); } public void run() String tname = super.getName(); try for(int i = 1; i<=5000;i++) System.out.println(tname+" "+i+" "+Math.random()); catch(Exception e) System.out.println("error"); class TestThread { public static void main(String[] args) MyThread t1 = new MyThread("t1"); MyThread t2 = new MyThread("t2"); t1.start(); t2.start(); } thread t1, t2 各執行5000次迴圈,在各個電腦產生的執行順序也會因為各個電腦當時的CPU效能而有所不同
8
說明 新的執行緒物件(new MyThread) 經由start()方法進入Runnable 區準備執行
排程後將選出來的執行緒丟入Running區執行 此時會觸發執行緒run()方法,執行實作內容(寫在run()內的程式碼) 若執行緒未執行完成(例如取得的CPU時間已到卻還沒完成工作,或是程式呼叫了特定的方法) ,或是執行wait(),將會觸發Blocking event並進入Blocked區 被封鎖的執行緒解除封鎖(Unblocked)或者是或別的程式中呼叫了notify()方法之後,將再進入Runnable pool中等待下一次執行 若程式中呼叫了yield()方法(讓與),則是將執行權先給其他執行緒執行,不過其本身仍然還是在Runnable中
9
不能多重繼承時 (MyThread2.java)
public class MyThread2 implements Runnable { int i; public void run() i = 0; while (true) System.out.println(Thread.currentThread().getName() + " " + (++i) + " " +Math.random()); if ( i == 5000 ) break; } class TestThread2 { public static void main(String args[]) MyThread2 r = new MyThread2(); Thread t1 = new Thread(r); Thread t2 = new Thread(r); t1.setName(“t1"); t2.setName(“t2"); t1.start(); t2.start(); } 先宣告一個空的MyThread2類別物件r 建立t1, t2 執行緒物件實體,其對象為 MyThread2中的run()方法
10
如果執行緒間彼此有同步的考量 (MyThread3.java)
public class MyThread3 implements Runnable{ static int max=0; public void run() { for(int i = 1; i<=5000;i++) int tmp=max; System.out.println(Thread.currentThread().getName()+" "+ ++tmp); max=tmp; } class TestThread3 { public static void main(String args[]) MyThread3 r = new MyThread3(); Thread t1 = new Thread(r); Thread t2 = new Thread(r); t1.setName("t1"); t2.setName("t2"); t1.start(); t2.start(); } 各把max拿來加5000次 最後卻不一定等於10000 而且每次都不一樣, why?
11
方法加 synchronized 保證 addMax()這個方法 只有同時間只有一個process可以執行
public class MyThread4 implements Runnable{ static int max=0; public void run() { for(int i = 1; i<=5000;i++) addMax(); } private synchronized void addMax() int tmp=max; System.out.println(Thread.currentThread().getName() + " " + ++tmp); max=tmp; 保證 addMax()這個方法 只有同時間只有一個process可以執行
12
Applet上有數個動畫 } public void run() {
off_image = this.createImage(width,height); off_graphics =off_image.getGraphics(); off_graphics.setColor(getBackground()); while(running) cnt = (++cnt)%images.length; off_graphics.fillRect(0,0,width,height); off_graphics.drawImage(images[cnt],0,0,this); try { Thread.sleep(delay); } catch(Exception e) { break; repaint(); public void update(Graphics g){g.drawImage(off_image,0,0,this);} public void paint(Graphics g){update(g);} public void setRunning(boolean val){running=val;} class DBPicShower extends Panel implements Runnable { int cnt=-1; int delay; int width, height; Image[] images; boolean running; Image off_image; Graphics off_graphics; public DBPicShower(int fps, Image[] imgs, int w, int h){ delay =(fps<0)?100:(1000/fps); running = true; this.setSize(w,h); width =w; height = h; images = imgs;} public Dimension getPreerredSize(){return getMinimumSize();} public Dimension getMinimumSize(){return new Dimension(width,height);}
13
Applet上有數個動畫 this.setLayout(flowLayout1); images = new Image[10];
shower = new DBPicShower[4]; showerThread = new Thread[4]; for(int i=0;i<10;i++) {images[i]=this.getImage(getCodeBase(),"T"+(i+1)+".gif");} for(int i=0;i<4;i++) { shower[i] = new DBPicShower(20,images,500,500); showerThread[i] = new Thread(shower[i]); this.add(shower[i]); } //Component initialization private void jbInit() throws Exception { this.setLayout(flowLayout1 ); //Get Applet information public String getAppletInfo() { return "Applet Information"; //Get parameter info public String[][] getParameterInfo() { return null; public void start(){ {showerThread[i].start();} Applet上有數個動畫 public class Applet1 extends Applet { boolean isStandalone = false; String[] imgFile; Thread[] showerThread; DBPicShower[] shower; Image[] images; // VerticalFlowLayout verticalFlowLayout1 = new VerticalFlowLayout(); FlowLayout flowLayout1 = new FlowLayout(); //Get a parameter value public String getParameter(String key, String def) { return isStandalone ? System.getProperty(key, def) : (getParameter(key) != null ? getParameter(key) : def); } //Construct the applet public Applet1() { //Initialize the applet public void init() { try { jbInit(); catch(Exception e) { e.printStackTrace();
14
Your Turn 寫一個多執行緒程式,模擬三個人存同一個戶頭的錢,每次存1000圓,總共存10次 每次存完讓該Thread睡個幾毫秒
Thread.sleep((long)(Math.random()*10)); 這個方法需要處理exception,試用教過的例外處理 最後輸出的是隨機的存款出現的順序 確保最後出現的是三萬元 (可以每次顯示是誰存的,存多少,總額)
15
I/O Overview of I/O Streams Using the Streams File Streams
Wrap Streams InputStreams Your Turn Standard in and out Streams Pipe Streams SequenceInputStream Filter Streams Random Access File
16
Overview of I/O Streams
17
Overview of I/O Streams
讀取與寫出資料的演算法: 讀取 寫出 開啟資料流 while 還有資料 讀取資料 關閉資料流 寫出資料 在 Java 中,處理 IO 必須要 import java.io 這個 package 根據資料型態(字元或位元),可以分成二類:
18
Overview of I/O Streams
Character Streams 用來處理 16 位元資料,如:字元資料(unicode) Reader 和 Writer 是所有 character streams 的 abstract superclasses
19
Overview of I/O Streams
Byte Streams 用來處理 8 位元資料,如:執行檔、圖檔和聲音檔 InputStream 和 OutputStream 是所有 byte streams 的 abstract superclasses
20
Overview of I/O Streams
Understanding the IO Superclasses Reader 與 InputStream 有非常類似的 API,只是處理的資料型態不同,如: Reader 中的 methods int read() int read(char cbuf[]) int read(char cbuf[], int offset, int length) InputStream 中的 methods int read(byte buf[]) int read(byte buf[], int offset, int length)
21
Overview of I/O Streams
Writer 與 OutputStream Writer 中的 methods int write(int c) int write(char cbuf[]) int write(char cbuf[], int offset, int length) OutputStream 中的 methods int write(byte buf[]) int write(byte buf[], int offset, int length) 所以的 Streams 都一樣,在物件建立之後,就會自動開啟,而呼叫 close() 就可以關閉資料流
22
Using the Streams I/O 類型 資料流類別 說明 檔案(File) 功能在於存取檔案或檔案系統的內容 管線(Pipe)
FileReader FileWriter FileInputStream FileOutputStream 功能在於存取檔案或檔案系統的內容 管線(Pipe) PipedReader PipedWriter PipedInputStream PipedOutputStream 將某個程式(或 Thread)的輸出導入另一個程式的輸入 串接(Concatenate) 未提供 SequenceInputStream 將多個 input stream 串接到同一個 input stream
23
Using the Streams I/O 類型 資料流類別 說明 緩衝(Buffer) 讀寫時為資料緩衝區,可以減少存取原始資料的次數
BufferedReader BufferedWriter BufferedInputStream BufferedOutputStream 讀寫時為資料緩衝區,可以減少存取原始資料的次數 過濾(Filter) FilterReader FilterWriter FilterInputStream FilterOutputStream 這幾個類別都是 abstract class,定義了過濾讀寫資料的介面 物件序列化 (Object Serialization) 未提供 ObjectInputStream ObjectOutputStream 用來做物件序列化(Object Serialization)的動作
24
File 檔案與目錄之操作 在 Java 中建立 File 物件,就可以取得檔案的或資料夾的相關訊息,如:File f = new File(String); File() 建構元參數是檔案或是資料夾的路徑 例如: import java.io.*; File f = new File(“haha.txt”);
25
File File 的相關方法 public boolean isDirectory() public boolean isFile()
傳回目前 java.io.File 內所包含的這個名稱是否為一個資料夾 public boolean isFile() 傳回目前 java.io.File 內的名稱是否為一個檔案 public String[] list() 傳回目前資料夾內所包含的資料夾名稱及檔案名稱
26
檔案清單範例 Example: Dir.java import java.io.*; public class Dir {
public static void main(String[] args) { String[] filenames; File f = new File(args[0]); if ( f.isDirectory() ) { filenames = f.list(); for(int i=0; i< filenames.length; i++) System.out.println(filenames[i]); } else System.out.println(f + "is not a directory");
27
顯示檔案資訊 相關方法 public boolean exists(): 檢查檔案是否存在
public boolean canRead(): 是否可讀 public boolean canWrite(): 是否可寫 public getName(): 傳回該檔案的名稱 public long length(): 傳回檔案大小 public long lastModified(): 傳回檔案修改時間 public getPath(): 取得該檔案所屬的資料夾名稱 public getParent(): 取得該檔案所屬的父資料夾名稱 public getAbsoluteFile(): 傳回該檔案的絕對路徑
28
顯示檔案資訊範例 Exmaple: FileInfo.java File f = new File(args[0]);
if ( f.exists() ) { if ( f.isFile() ) System.out.print("File: "); else if(f.isDirectory()) System.out.print("Directory: "); System.out.println(f.getAbsoluteFile()); System.out.println("Length: " + f.length()); System.out.println("Readable: " + f.canRead()); System.out.println("Writable: " + f.canWrite()); } else System.out.println(f + " does not exist!");
29
更改檔案名稱 相關方法 public boolean renameTo(File dest) 功能:更改檔名
傳回值:true 成功,false 失敗
30
更改檔案名稱範例 Example: RenameFile.java File fs = new File(args[0]);
File fd = new File(args[1]); if ( fs.exists() ) { if ( !fd.exists() ) { if (fs.renameTo(fd)) { System.out.println(fs.getName() + " --> " + fd.getName()); System.out.println("1 file(s) has been renamed!"); } else System.out.println("Target name already exists!"); System.out.println("Wrong source name!");
31
File 相關方法 刪除檔案 public boolean delete() 功能:刪除檔案
傳回值:true 刪除成功;false 失敗 建立資料夾 boolean mkdir() boolean mkdirs() 建立資料夾,成功傳回 true;失敗 false
32
建立資料夾範例 Example: MakeDir.java import java.io.*; public class MakeDir {
public static void main(String[] args) { File dir = new File(args[0]); if ( !dir.exists() ) { boolean success = dir.mkdir(); // boolean success = dir.mkdirs(); System.out.println("Create "+dir+" is successed!!"); } else System.out.println(dir + " is already exist!!!");
33
FileReader / FileWriter
可利用 File 物件來當作 FileReader / FileWriter 的建構元參數 如: File inputFile = new File(“test.txt”); FileReader in = new FileReader(inputFile); 建立 FileReader 的物件之後可用 read() 來讀入 16-bit 字元 建立 FileWriter 的物件之後可用 write() 來寫出 16-bit 字元
34
FileReader public class Untitled1 { public Untitled1() { }
public static void main(String[] args) throws IOException { Untitled1 untitled11 = new Untitled1(); String[] filenames; FileReader in = new FileReader("c:\\ip.txt"); int c; while ((c = in.read()) != -1) System.out.println((char)c); in.close();
35
FileWriter public class Untitled1 { public Untitled1() { }
public static void main(String[] args) throws IOException { Untitled1 untitled11 = new Untitled1(); String[] filenames; FileReader in = new FileReader("c:\\ip.txt"); FileWriter out = new FileWriter("c:\\op.txt"); int c; while ((c = in.read()) != -1) out.write(c); in.close(); out.close();
36
Wrap Streams Wrap Streams – 包裹 Stream
以一種 stream 類別包裹另一種 stream 類別的情形,這樣可以將各種不同資料流中好用的 methods 合併使用 在讀取資料時,可以利用 BufferedReader 將來源包裹(wrap)起來,如此一來便可以使用呼叫 BufferedReader 中提供的 readLine() 來讀入一整行的資料
37
How to Wrap a Stream 想計算檔案中的行數
查 class FileRead ->沒有這種功能 -> 自己寫??? 又查到 class BufferedReader中有 public String readLine() throws IOException 怎麼辦??->包裝FileReader使成Buffered 查BufferedReader的Constructor BufferedReader (Reader in)
38
Example public class Untitled1 { public Untitled1() { }
public static void main(String[] args) throws IOException { Untitled1 untitled11 = new Untitled1(); String[] filenames; FileReader in = new FileReader("c:\\ip.txt"); BufferedReader br = new BufferedReader(in); String aline; int cnt=0; while ((aline = br.readLine())!=null ) { System.out.println(aline); cnt++; System.out.println(cnt); br.close();
39
Java讀寫檔案 BufferedReader 和 BufferedWriter
程式做 IO 動作時,使用 8K 左右大小之 Buffer 可得到最佳效能 BufferedReader 和 BufferedWriter 已經內建 Buffer 幫你做實際 IO 傳輸 buffer 處理 示範程式 使用 BufferedReader 讀出檔案內容
40
BufferedReader使用法 Constructor:
BufferedReader(Reader in); Reader 是一個 abstract 類別,不能被 new 出來,只能被繼承使用 查API可知道 Reader 被 FileReader 繼承。 而 FileReader 有一 Constructor 為 FileReader(String filename) 因此我們可以 new 一個 FileReader,當作參數傳入BufferedReader 之 Constructor
41
BufferedWriter使用法 Constructor:
BufferedWriter(Writer out); Writer 是一個 abstract 類別,不能被 new 出來,只能被繼承使用 查 API 可知到 Writer 被 FileWriter 繼承。 而 FileWriter 有一 Constructor 為 FileWriter(String filename) 因此我們可以 new 一個 FileWriter,當作參數傳入BufferedWriter 之 Constructor
42
為何Java如此大費周章 使用BufferedReader為何還要先產生FileReader,再指向檔案名稱?
BufferedReader br = new BufferedReader(new FileReader(“Test.txt”)); 因為 BufferedReader 和 BufferedWriter 不只可以做檔案處理之功能 只需置換 BufferedReader 之建構子,即可使用相同的API做出讀取鍵盤輸入和網路傳輸等功能 請注意 BufferedReader 和 BufferedWriter 的建構子為 Reader 和 Writer 之類別
43
InputStream / OutputStream
在 Java 中,相對應於 Reader / Writer,用來讀取和寫入位元或位元陣列 提供的方法: int read() -- 讀取一個位元 int read(byte[]) – 讀取位元陣列,當資料結尾時,傳回 -1 int read(byte[], int, int) void write(int) – 寫入整數 void write(byte[]) – 寫入 1 個位元組陣列 void write(byte[], int, int)
44
InputStream / OutputStream
對於位元的檔案 IO,Java 程式是開啟 FileOutputStream 和 FileInputStream 串流,如: FileOutputStream output = new FileOutputStream(file); … FileInputStream input = new FileInputStream(name); 參數可以是檔案路徑字串,或是 File 物件
45
Example public class Untitled1 { public Untitled1() { }
public static void main(String[] args) throws IOException { Untitled1 untitled11 = new Untitled1(); String[] filenames; FileInputStream input = new FileInputStream("c:\\ip.txt"); FileOutputStream output = new FileOutputStream("c:\\clone.jpg"); int b; while ( (b = input.read()) != -1 ) { output.write(b); System.out.println(b); // 顯示出一個 pixel 的數值 output.close(); input.close();
46
Your Turn 建立一個 Java 程式從命令參數列輸入個數和最大值 maxValue,然後使用亂數產生 0 ~ maxValue 的整數值,以每個整數間隔一個 Tab,每一列 5 個的方式將整數以字元方式寫入文字檔案中。 產生亂數的方式:Math.random() 記得處理例外 試利用 BufferedReader 中的 readLine() 來將剛剛產生的檔案內容讀出 See FileIO.java
Similar presentations