มัลติเธรดใน Java
แอปพลิเคชันใดๆ ก็สามารถมีกระบวนการ (อินสแตนซ์) ได้หลายกระบวนการ โดยแต่ละกระบวนการสามารถกำหนดให้เป็นเธรดเดียวหรือหลายเธรดก็ได้ ในบทช่วยสอนนี้ เราจะมาดูวิธีการทำงานหลายงานพร้อมกัน และเรียนรู้เพิ่มเติมเกี่ยวกับเธรดและการซิงโครไนซ์ระหว่างเธรด
เธรดเดี่ยวคืออะไร?
ด้ายเส้นเดียวเข้า. Java โดยพื้นฐานแล้วเป็นหน่วยการประมวลผลที่มีน้ำหนักเบาและเป็นหน่วยที่เล็กที่สุด Java ใช้เธรดโดยใช้ “คลาสเธรด” มีเธรดสองประเภท – เธรดผู้ใช้และเธรด daemon (เธรดเดมอนใช้เมื่อเราต้องการทำความสะอาดแอปพลิเคชันและใช้ในเบื้องหลัง) เมื่อแอปพลิเคชันเริ่มทำงาน เธรดผู้ใช้จะถูกสร้างขึ้น หลังจากนั้น เราสามารถสร้างเธรดผู้ใช้และเธรดเดมอนได้หลายเธรด
ตัวอย่างเธรดเดี่ยว:
package demotest; public class GuruThread { public static void main(String[] args) { System.out.println("Single Thread"); } }
ข้อดีของเธรดเดี่ยว:
- ลดค่าใช้จ่ายในแอปพลิเคชันเมื่อดำเนินการเธรดเดียวในระบบ
- นอกจากนี้ยังช่วยลดต้นทุนการบำรุงรักษาแอปพลิเคชันอีกด้วย
มัลติเธรดคืออะไร Java?
มัลติเธรด in Java เป็นกระบวนการดำเนินการเธรดสองเธรดหรือมากกว่าพร้อมกันเพื่อให้ใช้ CPU ได้อย่างเต็มประสิทธิภาพ แอปพลิเคชันมัลติเธรดจะดำเนินการเธรดสองเธรดหรือมากกว่าพร้อมกัน ดังนั้นจึงเรียกอีกอย่างว่า Concurrency ใน Java- แต่ละเธรดทำงานขนานกัน เธรด Mulitple ไม่ได้จัดสรรพื้นที่หน่วยความจำแยกกัน ดังนั้นจึงช่วยประหยัดหน่วยความจำ นอกจากนี้ การสลับบริบทระหว่างเธรดใช้เวลาน้อยลง
ตัวอย่างของมัลติเธรด:
package demotest; public class GuruThread1 implements Runnable { public static void main(String[] args) { Thread guruThread1 = new Thread("Guru1"); Thread guruThread2 = new Thread("Guru2"); guruThread1.start(); guruThread2.start(); System.out.println("Thread names are following:"); System.out.println(guruThread1.getName()); System.out.println(guruThread2.getName()); } @Override public void run() { } }
ข้อดีของมัลติเธรด:
- ผู้ใช้จะไม่ถูกบล็อคเนื่องจากเธรดเป็นอิสระ และเราสามารถดำเนินการหลายอย่างพร้อมกันได้
- เนื่องจากเธรดนั้นมีความเป็นอิสระ เธรดอื่นจะไม่ได้รับผลกระทบหากเธรดหนึ่งตรงตามข้อยกเว้น
วงจรชีวิตของเธรดใน Java
วงจรชีวิตของเธรด:
วงจรชีวิตของเธรดมีระยะต่างๆ ดังแสดงในแผนภาพด้านบน:
- ใหม่
- รันได้
- เล่น
- ที่รอ
- ตาย
- ใหม่: ในขั้นตอนนี้ เธรดจะถูกสร้างขึ้นโดยใช้คลาส "คลาสเธรด" ซึ่งจะคงอยู่ในสถานะนี้จนกระทั่งโปรแกรม เริ่มต้น ด้าย เป็นที่รู้จักกันว่าด้ายเกิด
- รันได้: ในหน้านี้ อินสแตนซ์ของเธรดถูกเรียกใช้ด้วยวิธีเริ่มต้น ตัวควบคุมเธรดถูกกำหนดให้กับตัวกำหนดเวลาเพื่อสิ้นสุดการดำเนินการ ขึ้นอยู่กับตัวกำหนดเวลาว่าจะรันเธรดหรือไม่
- เล่น: เมื่อเธรดเริ่มทำงาน สถานะจะเปลี่ยนเป็นสถานะ "กำลังทำงาน" ตัวกำหนดเวลาเลือกหนึ่งเธรดจากกลุ่มเธรด และเริ่มดำเนินการในแอปพลิเคชัน
- ที่รอ: นี่คือสถานะที่เธรดต้องรอ เนื่องจากมีเธรดหลายเธรดกำลังทำงานอยู่ในแอปพลิเคชัน จึงจำเป็นต้องซิงโครไนซ์ระหว่างเธรด ดังนั้น เธรดหนึ่งต้องรอจนกว่าเธรดอื่นจะดำเนินการได้ ดังนั้น สถานะนี้จึงเรียกว่าสถานะรอ
- ตาย: นี่คือสถานะเมื่อเธรดถูกยกเลิก เธรดอยู่ในสถานะกำลังทำงาน และทันทีที่ประมวลผลเสร็จสิ้น เธรดจะอยู่ใน "สถานะไม่ทำงาน"
วิธีการมัลติเธรดใน Java
วิธีการเธรดที่ใช้กันทั่วไปบางส่วน ได้แก่:วิธี | Descriptไอออน |
---|---|
เริ่ม () | วิธีการนี้จะเริ่มต้นการดำเนินการของเธรดและ JVM เรียกเมธอด run() บนเธรด |
นอนหลับ(int มิลลิวินาที) | วิธีการนี้จะทำให้เธรดเข้าสู่โหมดพัก ดังนั้นการดำเนินการของเธรดจะหยุดชั่วคราวเป็นเวลาหลายมิลลิวินาที จากนั้นเธรดจะเริ่มทำงานอีกครั้ง ซึ่งจะช่วยในการซิงโครไนซ์เธรด |
getName () | มันจะส่งคืนชื่อของเธรด |
setPriority (ลำดับความสำคัญใหม่ int) | มันเปลี่ยนลำดับความสำคัญของเธรด |
ผลผลิต () | มันทำให้เธรดปัจจุบันหยุดทำงานและเธรดอื่น ๆ เพื่อดำเนินการ |
ตัวอย่าง: ในโปรแกรมมัลติเธรดนี้ค่ะ Java ตัวอย่างเช่น เราจะสร้างเธรดและสำรวจวิธีการที่มีอยู่ภายในที่มีให้สำหรับเธรด
package demotest; public class thread_example1 implements Runnable { @Override public void run() { } public static void main(String[] args) { Thread guruthread1 = new Thread(); guruthread1.start(); try { guruthread1.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } guruthread1.setPriority(1); int gurupriority = guruthread1.getPriority(); System.out.println(gurupriority); System.out.println("Thread Running"); } }
คำอธิบายของรหัส:
- รหัสบรรทัด 2: เรากำลังสร้างคลาส “thread_Example1” ซึ่งใช้งานอินเทอร์เฟซ Runnable (ควรใช้งานโดยคลาสใดๆ ที่มีอินสแตนซ์ตั้งใจให้รันโดยเธรด)
- รหัสบรรทัด 4: จะแทนที่วิธีการรันของอินเทอร์เฟซที่รันได้ เนื่องจากจำเป็นต้องแทนที่วิธีการนั้น
- รหัสบรรทัด 6: ที่นี่เราได้กำหนดวิธีการหลักที่เราจะเริ่มต้นการทำงานของเธรด
- รหัสบรรทัด 7: ที่นี่เรากำลังสร้างชื่อเธรดใหม่เป็น "guruthread1" โดยการสร้างคลาสของเธรดใหม่
- รหัสบรรทัด 8: เราจะใช้วิธี "เริ่มต้น" ของเธรดโดยใช้อินสแตนซ์ "guruthread1" ที่นี่เธรดจะเริ่มดำเนินการ
- รหัสบรรทัด 10: ที่นี่เราใช้วิธี "sleep" ของเธรดโดยใช้อินสแตนซ์ "guruthread1" ดังนั้น เธรดจะเข้าสู่โหมดสลีปเป็นเวลา 1000 มิลลิวินาที
- รหัส 9-14: ที่นี่เราได้ใส่วิธี sleep ไว้ใน try catch block เนื่องจากมีการตรวจสอบข้อยกเว้นซึ่งเกิดขึ้น เช่น ข้อยกเว้นที่ถูกขัดจังหวะ
- รหัสบรรทัด 15: ที่นี่เรากำลังตั้งค่าลำดับความสำคัญของเธรดเป็น 1 จากลำดับความสำคัญใดก็ตาม
- รหัสบรรทัด 16: ที่นี่เราได้รับลำดับความสำคัญของเธรดโดยใช้ getPriority()
- รหัสบรรทัด 17: ที่นี่เรากำลังพิมพ์ค่าที่ดึงมาจาก getPriority
- รหัสบรรทัด 18: ที่นี่เรากำลังเขียนข้อความที่เธรดกำลังทำงานอยู่
เมื่อคุณรันโค้ดข้างต้น คุณจะได้ผลลัพธ์ต่อไปนี้:
Output:
5 คือลำดับความสำคัญของเธรด และการรันเธรดคือข้อความซึ่งเป็นเอาต์พุตของโค้ดของเรา
Java ด้าย Syncการทำให้เป็นพงศาวดาร
ในการทำงานแบบมัลติเธรด โปรแกรมจะทำงานแบบอะซิงโครนัส หากเธรดหนึ่งเขียนข้อมูลบางอย่างและอีกเธรดหนึ่งอ่านข้อมูลในเวลาเดียวกัน อาจทำให้แอปพลิเคชันเกิดความไม่สอดคล้องกันได้ เมื่อจำเป็นต้องเข้าถึงทรัพยากรที่ใช้ร่วมกันโดยเธรดสองเธรดหรือมากกว่านั้น จะใช้แนวทางการซิงโครไนซ์ Java ได้จัดเตรียมวิธีการแบบซิงโครไนซ์เพื่อใช้ในการดำเนินงานลักษณะการทำงานแบบซิงโครไนซ์
ในแนวทางนี้ เมื่อเธรดเข้าไปถึงภายในบล็อกที่ซิงโครไนซ์แล้ว เธรดอื่นจะไม่สามารถเรียกใช้เมธอดนั้นในอ็อบเจ็กต์เดียวกันได้ เธรดทั้งหมดต้องรอจนกว่าเธรดนั้นจะเสร็จสิ้นบล็อกที่ซิงโครไนซ์และออกมาจากบล็อกนั้น ด้วยวิธีนี้ การซิงโครไนซ์จึงช่วยในแอปพลิเคชันแบบมัลติเธรด เธรดหนึ่งต้องรอจนกว่าเธรดอื่นจะเสร็จสิ้นการดำเนินการเท่านั้น จึงจะอนุญาตให้เธรดอื่นดำเนินการได้
สามารถเขียนได้ในรูปแบบต่อไปนี้:
Synchronized(object) { //Block of statements to be synchronized }
มัลติเธรดใน Java ตัวอย่างโปรแกรม
ในมัลติเธรดนี้ Java ตัวอย่าง เราจะใช้สองเธรดและดึงชื่อของเธรด
ตัวอย่างที่ 1:
GuruThread1.java package demotest; public class GuruThread1 implements Runnable{ /** * @param args */ public static void main(String[] args) { Thread guruThread1 = new Thread("Guru1"); Thread guruThread2 = new Thread("Guru2"); guruThread1.start(); guruThread2.start(); System.out.println("Thread names are following:"); System.out.println(guruThread1.getName()); System.out.println(guruThread2.getName()); } @Override public void run() { } }
คำอธิบายของรหัส:
- รหัสบรรทัด 3: เราได้เรียนคลาส “GuruThread1” ซึ่งใช้ Runnable (ควรนำไปใช้โดยคลาสใดๆ ที่อินสแตนซ์ตั้งใจให้เรียกใช้งานโดยเธรด)
- รหัสบรรทัด 8: นี่เป็นวิธีการหลักของชั้นเรียน
- รหัสบรรทัด 9: ที่นี่เรากำลังสร้างอินสแตนซ์ของคลาส Thread และสร้างอินสแตนซ์ชื่อ "guruThread1" และสร้างเธรด
- รหัสบรรทัด 10: ที่นี่เรากำลังสร้างอินสแตนซ์คลาส Thread และสร้างอินสแตนซ์ชื่อ "guruThread2" และสร้างเธรด
- รหัสบรรทัด 11: เรากำลังเริ่มเธรดเช่น guruThread1
- รหัสบรรทัด 12: เรากำลังเริ่มเธรดเช่น guruThread2
- รหัสบรรทัด 13: แสดงผลข้อความเป็น “ชื่อเธรดมีดังต่อไปนี้:”
- รหัสบรรทัด 14: รับชื่อของเธรด 1 โดยใช้วิธี getName() ของคลาสเธรด
- รหัสบรรทัด 15: รับชื่อของเธรด 2 โดยใช้วิธี getName() ของคลาสเธรด
เมื่อคุณรันโค้ดข้างต้น คุณจะได้ผลลัพธ์ต่อไปนี้:
Output:
ชื่อเธรดจะถูกส่งออกที่นี่เป็น
- Guru1
- Guru2
2 ตัวอย่าง:
ในมัลติเธรดนี้ใน Java ตัวอย่าง เราจะเรียนรู้เกี่ยวกับวิธีการแทนที่ run() และ start() วิธีการของอินเทอร์เฟซที่รันได้ และสร้างเธรดสองเธรดของคลาสนั้นและรันตามลำดับ
นอกจากนี้ เรายังเรียนอยู่ 2 ชั้นเรียน ได้แก่
- หนึ่งซึ่งจะใช้อินเทอร์เฟซที่รันได้และ
- อีกอันหนึ่งซึ่งจะมีวิธีการหลักและดำเนินการตามนั้น
package demotest; public class GuruThread2 { public static void main(String[] args) { // TODO Auto-generated method stub GuruThread3 threadguru1 = new GuruThread3("guru1"); threadguru1.start(); GuruThread3 threadguru2 = new GuruThread3("guru2"); threadguru2.start(); } } class GuruThread3 implements Runnable { Thread guruthread; private String guruname; GuruThread3(String name) { guruname = name; } @Override public void run() { System.out.println("Thread running" + guruname); for (int i = 0; i < 4; i++) { System.out.println(i); System.out.println(guruname); try { Thread.sleep(1000); } catch (InterruptedException e) { System.out.println("Thread has been interrupted"); } } } public void start() { System.out.println("Thread started"); if (guruthread == null) { guruthread = new Thread(this, guruname); guruthread.start(); } } }
คำอธิบายของรหัส:
- รหัสบรรทัด 2: ที่นี่เรากำลังเรียนคลาส “GuruThread2” ซึ่งจะมีเมธอดหลักอยู่ในนั้น
- รหัสบรรทัด 4: ที่นี่เรากำลังใช้วิธีหลักของชั้นเรียน
- รหัสบรรทัด 6-7: ที่นี่เรากำลังสร้างอินสแตนซ์ของคลาส GuruThread3 (ซึ่งสร้างขึ้นในบรรทัดด้านล่างของโค้ด) เป็น "threadguru1" และเรากำลังเริ่มเธรด
- รหัสบรรทัด 8-9: ที่นี่เรากำลังสร้างอีกตัวอย่างหนึ่งของคลาส GuruThread3 (ซึ่งสร้างขึ้นในบรรทัดด้านล่างของโค้ด) เป็น "threadguru2" และเรากำลังเริ่มเธรด
- รหัสบรรทัด 11: ที่นี่เรากำลังสร้างคลาส “GuruThread3” ซึ่งใช้งานอินเทอร์เฟซที่รันได้ (ควรใช้งานโดยคลาสใดก็ตามที่มีอินสแตนซ์ตั้งใจให้รันโดยเธรด)
- รหัสบรรทัด 13-14: เรากำลังรับตัวแปรคลาสสองตัวโดยตัวหนึ่งเป็นคลาสเธรดประเภทและอีกตัวเป็นคลาสสตริง
- รหัสบรรทัด 15-18: เรากำลังแทนที่ตัวสร้าง GuruThread3 ซึ่งรับหนึ่งอาร์กิวเมนต์เป็นประเภทสตริง (ซึ่งเป็นชื่อเธรด) ที่ได้รับการกำหนดให้กับตัวแปรคลาส guruname และด้วยเหตุนี้ชื่อของเธรดจึงถูกจัดเก็บ
- รหัสบรรทัด 20: ที่นี่เรากำลังแทนที่เมธอด run() ของอินเทอร์เฟซที่รันได้
- รหัสบรรทัด 21: เรากำลังส่งออกชื่อเธรดโดยใช้คำสั่ง println
- รหัสบรรทัด 22-31: ที่นี่เรากำลังใช้ for loop โดยมีตัวนับเริ่มต้นเป็น 0 และไม่ควรน้อยกว่า 4 (เรารับตัวเลขใดๆ ก็ได้ ดังนั้นที่นี่ลูปจะทำงาน 4 ครั้ง) และเพิ่มตัวนับ เรากำลังพิมพ์ชื่อเธรดและทำให้เธรดเข้าสู่โหมดสลีปเป็นเวลา 1000 มิลลิวินาทีภายในบล็อก try-catch เนื่องจากวิธีสลีปทำให้เกิดข้อยกเว้นในการตรวจสอบ
- รหัสบรรทัด 33: ที่นี่เรากำลังแทนที่วิธีการเริ่มต้นของอินเทอร์เฟซที่รันได้
- รหัสบรรทัด 35: เรากำลังแสดงข้อความ "เริ่มต้นเธรด"
- รหัสบรรทัด 36-40: ที่นี่เรากำลังใช้เงื่อนไข if เพื่อตรวจสอบว่า guruthread ตัวแปรคลาสมีค่าอยู่หรือไม่ หากเป็นโมฆะ เรากำลังสร้างอินสแตนซ์โดยใช้คลาสเธรดซึ่งใช้ชื่อเป็นพารามิเตอร์ (ค่าที่ได้รับมอบหมายในตัวสร้าง) หลังจากนั้นเธรดจะเริ่มทำงานโดยใช้เมธอด start()
เมื่อคุณรันโค้ดข้างต้น คุณจะได้ผลลัพธ์ดังต่อไปนี้:
เอาท์พุต:
มีสองเธรด ดังนั้นเราจึงได้รับข้อความสองครั้งว่า "เริ่มต้นเธรด"
เราได้รับชื่อของเธรดตามที่เราส่งออกไป
มันจะเข้าสู่ for loop โดยที่เรากำลังพิมพ์ตัวนับและชื่อเธรดและตัวนับเริ่มต้นด้วย 0
การวนซ้ำดำเนินการสามครั้งและในระหว่างเธรดจะถูกพักเป็นเวลา 1000 มิลลิวินาที
ดังนั้น อันดับแรก เราได้ guru1 จากนั้น guru2 และ guru2 อีกครั้ง เนื่องจากเธรดสลีปที่นี่เป็นเวลา 1000 มิลลิวินาที จากนั้น guru1 ถัดไป และอีกครั้ง guru1 เธรดสลีปเป็นเวลา 1000 มิลลิวินาที ดังนั้นเราจึงได้ guru2 และ guru1
สรุป
ในบทช่วยสอนนี้ เราเห็นแอปพลิเคชันแบบมัลติเธรดใน Java และวิธีการใช้งานเธรดเดี่ยวและมัลติเธรด Java.
- อธิบายมัลติเธรดใน Java:ในการมัลติเธรด ผู้ใช้จะไม่ถูกบล็อกเนื่องจากเธรดเป็นอิสระและสามารถดำเนินการหลายอย่างพร้อมกันได้
- ระยะต่างๆ ของวงจรชีวิตของเส้นด้าย ได้แก่
- ใหม่
- รันได้
- เล่น
- ที่รอ
- ตาย
- เรายังได้เรียนรู้เกี่ยวกับ การประสาน ระหว่างเธรดซึ่งช่วยให้แอปพลิเคชันทำงานได้อย่างราบรื่น
- การเขียนโปรแกรมแบบมัลติเธรดใน Java ทำให้งานแอปพลิเคชันอื่น ๆ ง่ายขึ้น