MultiThread
Modern Programming Languages
10
โปรแกรมแบบมัลติเธรด (MultiThread) 10.1
โปรแกรมแบบมัลติเธรด
คุณสมบัติเดนอีกอยางหนึ่งของภาษาจาวาคือ ความสามารถในการพัฒนาโปรแกรมแบบมัลติเธรด คือ สามารถทํางานหลายงานพรอมกันได ซึ่งอาศัยหลักการทํางานแบบมัลติทาสกิ้ง (MultiTasking) โดยระบบมัลติ ทาสกิ้งอนุญาติใหผูใชสามารถสงโปรแกรมใหระบบปฏิบัติการทํางานไดมากกวาหนึ่งโปรแกรมพรอมกัน ซึ่งแตละ โปรแกรมที่รันอยูในระบบปฏิบัติการจะสราง process ขึ้นมา และในระบบปฏิบัติการแบบมัลติทาสกิ้งจะมี process หลายๆ process เขาคิวรอการทํางาน โดยระบบปฏิบัติการจะกําหนดลําดับของการทํางานของ process เอง ในโปรแกรมแบบมัลติเธรดจะเปนการทํางานพรอมกันโดยที่แตละงาน (process) จะเรียกวา เธรด (thread) ซึ่งแตกตางจาก process ที่ทํางานภายใตระบบปฏิบัติการแบบมัลติทาสกิ้งตรงที่ ในแตละเธรดจะทํางาน อยางเปนอิสระตอกัน ในแตละ thread มีสวนที่ใชขอมูลรวมกัน (share variable) โปรแกรมภาษาจาวาสามารถกําหนดการทํางานเปนแบบเธรดไดโดยการสรางอบบเจกตของคลาสใดๆ ใหทํางานแบบเธรด ซึ่งจะทําใหสามารถสรางรันโปรแกรมของออบเจกตแบบเธรดหลายๆออบเจกตไดพรอมกัน โดยภาษาจาวาจะมีตัวจัดตารางเวลา(Thread Scheduler) ของออบเจกตแบบเธรด เพื่อจัดลําดับการทํางานของ แตละออบเจกตแบบเธรด 10.2
การสรางคลาสแบบเธรด (Thread)
คลาสแบบเธรด คือคลาสที่สืบทอดคุณสมบัติจากคลาส Thread หรือคลาสที่ implements อินเตอรเฟส Runnable ในการสรางทั้งสองรูปแบบจะตองมีการ overriding เมธอด run() เพื่อระบุการทํางานแบบ thread ที่ ตองการออบเจกตที่ถูกสรางขึ้นจากคลาสแบบ Thread เรียกวา ออบเจกตแบบ Runnable interface Runnable void run()
implements
คลาสแบบ thread void run()
ถาไมมีเมธอด run() คอมไพลไมผาน ***ฉะนั้นตองมีการ overriding เมธอด run()
Thread void run()
คลาสแบบ thread
extends
void run()
เพื่อระบุการทํางานแบบ thread ในการสรางทั้ง 2 แบบ ถาไมมีเมธอด run() คอมไพลผาน แตไมระบุการทํางาน
[email protected]
1/10
MultiThread
Modern Programming Languages
10.2.1 การ extends คลาส Thread
รูปแบบ
โดยที่เมธอด run() จะประกอบดวยการทํางานที่มีลักษณะแบบเธรด การสรางและเริ่มตนการทํางานของออบเจกตเธรด ทําไดดังนี้
• •
ในบรรทัดที่ 1 และ 2 เปนการสรางออบเจกตชนิด Thread ชื่อ objName1 และ objName2 ตามลําดับ ในบรรทัดที่ 3 และ 4 เปนการเรียกใชงานเมธอด start() เพื่อสั่งใหออบเจกตชนิด Thread เริ่ม ทํางาน โดยการทํางานจะไปเรียกการทํางานที่เมธอด run() โดยการทํางานจะเกิดขึ้นพรอมๆกัน ดังรูปดานลาง objName1.start() objName2.start()
•
หากออบเจกตชนิด Thread ไมมีการเรียกใชงานเมธอด start() จะไมเกิดการทํางานแบบเธรด เชน เมื่อเรียกใชคําสั่งดังนี้ o o
•
objName1.run(); objName2.run();
จะเปนการทํางานตามคําสั่ง คือ เมื่อทําคําสั่ง objName1.run() เสร็จแลว จึงไปทํางานตอที่คําสั่ง objName2.run(); ดังรูปดานลาง objName1.run() objName2.run()
[email protected]
2/10
MultiThread
Modern Programming Languages
ตัวอยาง การสรางออบเจกตชนิด Thread ดวยการ extends คลาส Thread
ผลลัพธที่ไดจะไมเหมือนกันเสมอไป ตองลองรันโปรแกรมเพื่อดูผลที่เกิดขึ้น การทํางานในแตละเธรดจะ เกิดขึ้นพรอมๆ กัน 10.2.2 การ implements อินเตอรเฟส Runnable
รูปแบบ
ในการสรางลักษณะนี้จะตองมีการสรางออบเจกตของคลาส Thread กอน โดยคลาส Thread มีคอน สตรัคเตอร ทําการรับพารามิเตอรออบเจกตของ Runnable มีรูปแบบดังนี้
[email protected]
3/10
MultiThread
Modern Programming Languages
การสรางและเริ่มตนการทํางาน thread ทําไดดังนี้
• • •
ในบรรทัดที่ 1 และ 2 เปนการสรางออบเจกตชนิด Runnable ชื่อ objRunnable1 และ objRunnable2 ตามลําดับ ในบรรทัดที่ 3 และ 4 การสรางออบเจกตชนิด Thread โดยคอนสตรัคเตอรของคลาส Thread ทํา การรับพารามิเตอรออบเจกตของ Runnable ที่ไดสรางไวแลว ในบรรทัดที่ 5 และ 6 เปนการเรียกใชงานเมธอด start() เพื่อสั่งใหออบเจกตชนิด Thread เริ่ม ทํางาน โดยการทํางานจะไปเรียกการทํางานที่เมธอด run() ซึ่งเมธอด run() นี้อยูในคลาสซึ่ง implements อินเตอรเฟส Runnable โดยการทํางานจะเกิดขึ้นพรอมๆกัน
ตัวอยาง การสรางออบเจกตชนิด Thread ดวยการ implements อินเตอรเฟส Runnable โดยการทํา การปรับปรุงจากตัวอยางขางตน
[email protected]
4/10
MultiThread
Modern Programming Languages
การสรางคลาสแบบเธรดทั้งสองวิธีมีขอดีตางกันดังนี้ • การสรางคลาสโดยการสืบทอดคลาสที่ชื่อ Thread จะเปนการเขียนโปรแกรมที่สั้นกวา • การสรางคลาสโดย implement อินเตอรเฟส Runnable จะมีหลักการเชิงออปเจ็คที่ดีกวา เนื่องจาก คลาสดังกลาวไมใชคลาสที่สืบทอดมาจาก Thread โดยตรง นอกจากนี้ยังทําใหการเขียนโปรแกรมมี รูปแบบเดี่ยวกันเสมอ เพราะคลาสในจาวาจะสืบทอดไดเพียงคลาสเดียว 10.3
วัฏจักรการทํางานของ Thread New start scheduler
wake up notify() I/O Completer
run() Running
Runnable
Block
Dead
sleep wait() block on I/O
•
New เปนสถานะเมื่อมีการสรางออบเจกตคลาสแบบ thread แตยังไมเริ่มทํางาน
•
Runnable เปนสถานะที่ออบเจกตแบบ thread พรอมที่จะทํางาน ซึ่งเกิดจากสาเหตุดังนี้
การเรียกใชเมธอด start() o การกลับมาจากสถานะ Block • Running เปนสถานะที่รันคําสั่งของออบเจกตแบบ thread เมื่อตัวจัดตารางเวลา จัดลําดับการทํางาน ของ thread โดยทํางานตามชุดคําสั่งในเมธอด run() • Block เปนสถานะที่เกิดจากเหตุการณดังนี้ o มีการเรียกใชเมธอด sleep() o มีการเรียกใชเมธอด wait() o ออบเจกตแบบเธรด รอรับการทํางานที่เปนอินพุตหรือเอาตพุต ออบเจกตแบบ thread ที่อยูในสถานะ Block จะกลับเขาสูสถานะ Runnable ไดอีกครั้งหนึ่งเมื่อมีเหตุ การดังนี้ o เมื่อหมดเวลาที่กําหนดในเมธอด sleep() o เมื่อมีออบเจกต อื่นมาเรียกเมธอด notify() หรือ notifyAll() ในกรณีที่ถูก Block ดวยเมธอด o
wait()
เมื่อมีการสงขอมูลที่เปนอินพุตหรือเอาตพุต ในกรณีที่ออบเจกตแบบเธรด รอรับการทํางานที่ เปนอินพุตหรือเอาตพุต Dead เปนสถานะสิ้นสุดการทํางานของ thread เนื่องจากสิ้นสุดการทํางานของเมธอด run() หรือเมื่อมี การสงออบเจกตประเภท Exception o
•
[email protected]
5/10
MultiThread
10.4
เมธอดของคลาส Thread •
public void run() เปนเมธอดที่ถูกเรียกใชเมื่อ thread อยูในสภาวะที่พรอมทํางาน
•
public void start() เปนเมธอดที่เรียกใชงานเพื่อกําหนดใหออบเจกตแบบ thread เริ่มทํางาน โดย
• •
• • • • •
10.5
Modern Programming Languages
เขาสูสถานะ Runnable public void stop() เปนเมธอดที่กําหนดใหออบเจกตแบบ thread หยุดการทํางาน ไมรอใหสิ้นสุด การทํางานเอง public void wait() ใหออบเจกตแบบ thread เขาสูสถานะ Block จนกวาจะถูก thread อื่นเรียก เมธอด notify() จึงจะกลับมาทํางานได แตการเรียกเมธอด wait() จะตองถูกเรียกในเมธอดที่ระบุ เปน synchronized เทานั้น public void notify() ใชสําหรับใหออบเจกตแบบ thread ที่กําลังทํางานอยู บอกให thread ที่อยู ในสถานะ Block รูวาสามารถเขามาทํางานตอได public String getName() เปนเมธอดที่ใชในการรับชื่อของ thread public static void sleep(long mills) throws InterruptedException เปนเมธอดที่กําหนดใหออบเจกตแบบ thread ที่อยูในสถานะ Running หยุดการทํางาน
คือเขาสู
สถานะ Block เปนเวลา mills มิลลิวินาที public static void yield() เปนเมธอดที่ทําใหออบเจกตแบบ thread ที่กําลังทํางานอยูออกจาก สถานะ Running ไปสูสถานะ Runnable เพื่อให thread เขามาทํางาน
Synchronization
กรณีที่ออบเจกตแบบเธรดใชขอมูลรวมกัน อาจเกิดปญหาที่เรียกวา Race Condition ขึ้นได ซึ่งเปนกรณีที่ ออบเจกตแบบเธรดตางแยงกันจัดการขอมูลทําใหไดขอมูลที่ผิดพลาด เราสามารถใชคียเวิรด synchronized เพื่อ ชวยล็อกชุดคําสั่งที่ออบเจกตแบบเธรดตองทํางานพรอมกันไวได UnSynchronized
Synchronized
count() count() count() count()
[email protected]
6/10
MultiThread
Modern Programming Languages
ตัวอยาง การใชคียเวิรด synchronized ในการล็อกชุดคําสั่งที่ออบเจกตแบบเธรดตองทํางานพรอมกันไว ได โดยโปรแกรมนี้ทําการนับตัวเลข
ผลที่ได
•
ในบรรทัดที่ 23 และ 24 ทําการสรางออบเจกตชนิด Thread และเรียกใชงานเมธอด start() นั่นคือ การสั่งใหเธรดเริ่มทํางานจากโปรแกรม หากไมพิจารณาเมธอด count() ซึ่งถูกระบุขางหนาดวยคีย เวิรด synchronized ผลลัพธที่ไดนาจะเกิดการทํางานออบเจกตชื่อ “A” และออบเจกตชื่อ “B” ทํางานไปพรอมกัน แตเนื่องจากเมธอด run() เรียกใชงานในเมธอด count() ซึ่งเมธอดนี้ถูกระบุ ดวยคียเวิรด synchronized ทําใหผลลัพธที่ไดเกิดการทํางานออบเจกตชื่อ “A” เสร็จกอน จึงเขา ไปทํางานออบเจกตชื่อ “B”
[email protected]
7/10
MultiThread
10.6
Modern Programming Languages
Deadlock
ในกรณีที่ออบเจกตแบบเธรดถูกเรียกใชงานเมธอด wait() โดยไมมี thread ใดเรียกเมธอด notify() ทํา ใหไมสามารถมีการทํางานใดๆเกิดขึ้นได จึงทําใหเกิดเหตุการณที่เรียกวา Deadlock
[email protected]
8/10
MultiThread
Modern Programming Languages
แบบฝกหัด 1.
สราง ThreadFinal.java public class ThreadFinal extends Thread { public ThreadFinal(String n) { super(n); } public void run() { for (int i=0;i<5;i++) { System.out.println(getName()); try { sleep(100); } catch( InterruptedException e ) { } } } public static void main(String args[]) { for (int i=0;i<20;i++) new ThreadFinal("Thread:"+String.valueOf(i)).start(); } } จากโปรแกรมขางบน เปลี่ยนใหเปนการใช implements อินเตอรเฟส Runnable ดังนี้ class ThreadFinal implements Runnable {
} public static void main(String args[]) { //จงทําใหโปรแกรมทํางานเหมือนกับโปรแกรมขางบน
} }
[email protected]
9/10
MultiThread 2.
Modern Programming Languages
สรางโปรแกรมนาฬิกา ใหตัวอยางโปรแกรมในการแสดงเวลา ดังนี้ สราง Clock.htm <APPLET CODE="Clock.class" WIDTH=400 HEIGHT=400>
สราง Clock.java import java.applet.Applet; import java.awt.*; import java.util.Date; public class Clock extends Applet { Date date; int hour,minute,second; Font f = new Font("MonoSpaced",Font.BOLD,64); public void init() { } public void paint( Graphics g ) { date = new Date(); int hour= date.getHours(); int minute= date.getMinutes(); int second= date.getSeconds(); String time = String.valueOf(hour)+":" +String.valueOf(minute)+":"+String.valueOf(second); g.setFont(f); g.setColor( Color.blue ); g.drawString( time, 50, 50 ); } }
จากตัวอยางโปรแกรมขางตน การแสดงผลเวลายังไมเปน real time คือเปนการแสดงผลเวลา ขณะรันโปรแกรม หรือมีการปรับ ขนาดหนาจอเทานั้น ใหนักศึกษาทําการแสดงผลเวลาแบบ real time พรอมทั้งเพิ่ม การแสดงผล วันเดือนป ดวย ***ใชการสรางออบเจกตแบบ thread ชวย
[email protected]
10/10