การวิเคราะห์ที่ครอบคลุมที่สุดของการใช้งานมัลติเธรด Java หากคุณยังไม่ได้ทำการวิจัยเชิงลึกเกี่ยวกับกลไกมัลติเธรดของ Java บทความนี้สามารถช่วยให้คุณเข้าใจหลักการและวิธีการใช้งานของ Java Multithreading ได้อย่างละเอียดยิ่งขึ้น
1. สร้างเธรด
มีสองวิธีในการสร้างเธรดใน Java: การใช้คลาสเธรดและใช้อินเทอร์เฟซ Runnable เมื่อใช้อินเทอร์เฟซ Runnable คุณต้องสร้างอินสแตนซ์เธรด ดังนั้นไม่ว่าคุณจะสร้างเธรดผ่านคลาสเธรดหรืออินเทอร์เฟซที่เรียกใช้งานได้คุณต้องสร้างอินสแตนซ์ของคลาสเธรดหรือคลาสย่อย ตัวสร้างเธรด:
วิธีที่ 1: สืบทอดคลาสเธรดและเขียนทับวิธีการเรียกใช้
ชั้นเรียนสาธารณะ ThreadDemo1 {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {demo d = demo ใหม่ (); D.Start (); สำหรับ (int i = 0; i <60; i ++) {system.out.println (thread.currentthread (). getName ()+i); }}} การสาธิตคลาสขยายเธรด {public void run () {สำหรับ (int i = 0; i <60; i ++) {system.out.println (thread.currentthread (). getName ()+i); -วิธีที่ 2:
ชั้นเรียนสาธารณะ ThreadDemo2 {โมฆะคงที่สาธารณะหลัก (String [] args) {demo2 d = demo2 ใหม่ (); เธรด t = เธรดใหม่ (d); T.Start (); สำหรับ (int x = 0; x <60; x ++) {system.out.println (thread.currentthread (). getName ()+x); }}} คลาส Demo2 ใช้งาน runnable {public void run () {สำหรับ (int x = 0; x <60; x ++) {system.out.println (thread.currentthread (). getName ()+x); -2. วงจรชีวิตของเธรด
เช่นเดียวกับผู้คนที่มีการคลอดวัยชราความเจ็บป่วยและความตายเธรดก็ต้องผ่านสี่รัฐที่แตกต่างกัน: เริ่มต้น (รอ) วิ่งระงับและหยุด ทั้งสี่สถานะสามารถควบคุมได้โดยวิธีการในคลาสเธรด ต่อไปนี้เป็นวิธีที่เกี่ยวข้องกับสี่สถานะในคลาสเธรด
// เริ่มเธรด
PublicVoid Start ();
PublicVoid Run ();
// ระงับและปลุกเธรด
PublicVoid Resume (); // ไม่แนะนำให้ใช้
PublicVoid Suspend (); // ไม่แนะนำให้ใช้
Publicstaticvoid sleep (มิลลิสยาว);
PublicStaticVoid Sleep (ยาวมิลลิส, Nanos int);
// ยกเลิกเธรด
PublicVoid Stop (); // ไม่แนะนำให้ใช้
publicvoid interrupt ();
// รับสถานะเธรด
PublicBoolean isalive ();
Publicboolean isinterrupted ();
Publicstaticboolean ขัดจังหวะ ();
// วิธีเข้าร่วม
PublicVoid เข้าร่วม () พ่น InterruptedException;
หลังจากสร้างเธรดแล้วจะไม่เรียกใช้รหัสในวิธีการเรียกใช้ทันที แต่อยู่ในสถานะรอ เมื่อเธรดอยู่ในสถานะรอคุณสามารถใช้เมธอดเธรดคลาสเพื่อตั้งค่าคุณสมบัติต่าง ๆ ของเธรดเช่นลำดับความสำคัญของเธรด (setPriority) ชื่อเธรด (setName) และประเภทเธรด (setDaemon) ฯลฯ
หลังจากเรียกวิธีการเริ่มต้นเธรดจะเริ่มดำเนินการรหัสในวิธีการเรียกใช้ เธรดเข้าสู่สถานะการทำงาน คุณสามารถใช้วิธี isalive ของคลาสเธรดเพื่อตรวจสอบว่าเธรดอยู่ในสถานะการทำงานหรือไม่ เมื่อเธรดอยู่ในสถานะที่กำลังทำงานอยู่จะส่งกลับจริง เมื่อ Isalive ส่งคืนเท็จเธรดอาจอยู่ในสถานะรอหรืออยู่ในสถานะหยุด รหัสต่อไปนี้แสดงให้เห็นถึงการสลับระหว่างสถานะสามสถานะของการสร้างการรันและการหยุดเธรดและส่งออกค่า return isalive ที่สอดคล้องกัน
เมื่อเธรดเริ่มดำเนินการวิธีการเรียกใช้เธรดจะไม่ออกจนกว่าวิธีการเรียกใช้จะถูกเรียกใช้งาน อย่างไรก็ตามในระหว่างการดำเนินการของเธรดเธรดสามารถหยุดได้ชั่วคราวโดยสองวิธี สองวิธีนี้ระงับและนอนหลับ หลังจากระงับเธรดด้วยการระงับเธรดสามารถปลุกได้ด้วยวิธีการเรซูเม่ หลังจากใช้การนอนหลับเพื่อนอนหลับเธรดสามารถใส่ในสถานะพร้อมหลังจากเวลาที่ตั้งไว้ (หลังจากที่เธรดนอนหลับเธรดอาจไม่ทำงานทันที แต่มันก็เข้าสู่สถานะพร้อมและรอให้ระบบกำหนดเวลา)
มีสองสิ่งที่ควรทราบเมื่อใช้วิธีการนอนหลับ:
1. วิธีการนอนหลับมีสองแบบโอเวอร์โหลด หนึ่งในรูปแบบโอเวอร์โหลดไม่เพียง แต่ตั้งค่าเป็นมิลลิวินาทีเท่านั้น แต่ยังรวมถึงนาโนวินาที (1,000,000 นาโนวินาทีเท่ากับ 1 มิลลิวินาที) อย่างไรก็ตามเครื่องเสมือน Java บนแพลตฟอร์มระบบปฏิบัติการส่วนใหญ่ไม่สามารถแม่นยำกับนาโนวินาทีดังนั้นหากมีการตั้งค่า nanoseconds สำหรับการนอนหลับเครื่องเสมือน Java จะใช้เวลามิลลิวินาทีใกล้กับค่านี้มากที่สุด
2. เมื่อใช้วิธีการนอนหลับคุณต้องใช้โยนหรือลอง {…} จับ {…} เนื่องจากวิธีการเรียกใช้ไม่สามารถใช้โยนได้คุณสามารถใช้ลอง {…} catch {…} เมื่อเธรดนอนหลับการนอนหลับจะโยนข้อยกเว้น InterruptedException เมื่อขัดจังหวะเธรดโดยใช้วิธีการขัดจังหวะ วิธีการนอนหลับถูกกำหนดดังนี้:
PublicStaticVoid Sleep (ยาวมิลลิส) พ่นการขัดจังหวะการรับรู้
PublicStaticVoid Sleep (Long Millis, Int Nanos) พ่น InterruptedException
มีสามวิธีในการยุติเธรด
1. ใช้ธงทางออกเพื่อให้ออกจากเธรดตามปกตินั่นคือเธรดจะสิ้นสุดลงเมื่อวิธีการเรียกใช้เสร็จสมบูรณ์
2. ใช้วิธีการหยุดเพื่อยุติเธรด (วิธีนี้ไม่แนะนำให้ใช้เนื่องจากการหยุดนั้นเหมือนกับการระงับและดำเนินการต่อและอาจมีผลลัพธ์ที่คาดเดาไม่ได้)
3. ใช้วิธีการขัดจังหวะเพื่อขัดจังหวะเธรด
1. ใช้ธงทางออกเพื่อยุติเธรด
เมื่อใช้วิธีการเรียกใช้เธรดจะออก แต่บางครั้งวิธีการวิ่งก็ไม่สิ้นสุด ตัวอย่างเช่นการใช้เธรดเพื่อฟังคำขอไคลเอนต์ในโปรแกรมเซิร์ฟเวอร์หรืองานอื่น ๆ ที่ต้องใช้การประมวลผลแบบวนซ้ำ ในกรณีนี้งานเหล่านี้มักจะอยู่ในลูปเช่นขณะวนรอบ หากคุณต้องการให้ลูปทำงานตลอดไปคุณสามารถใช้ในขณะที่ (จริง) {…} เพื่อจัดการกับมัน อย่างไรก็ตามหากคุณต้องการที่จะออกไปในขณะที่ลูปออกภายใต้เงื่อนไขที่แน่นอนวิธีที่ตรงที่สุดคือการตั้งค่าธงประเภทบูลีนและควบคุมว่าในขณะที่ลูปออกโดยการตั้งค่าสถานะนี้เป็นจริงหรือเท็จ นี่คือตัวอย่างของการยกเลิกเธรดโดยใช้ธงทางออก
ฟังก์ชั่นของวิธีการเข้าร่วมคือการเปลี่ยนเธรดการดำเนินการแบบอะซิงโครนัสเป็นการดำเนินการแบบซิงโครนัส กล่าวคือเมื่อวิธีการเริ่มต้นของอินสแตนซ์เธรดถูกเรียกวิธีการจะกลับมาทันที หากจำเป็นต้องใช้ค่าที่คำนวณโดยเธรดนี้หลังจากเรียกใช้วิธีการเริ่มต้นจะต้องใช้วิธีการเข้าร่วม หากคุณไม่ได้ใช้วิธีการเข้าร่วมจะไม่สามารถรับประกันได้ว่าเมื่อคำสั่งที่อยู่เบื้องหลังวิธีการเริ่มต้นจะดำเนินการเธรดจะถูกดำเนินการ หลังจากใช้วิธีการเข้าร่วมโปรแกรมจะไม่ถูกเรียกใช้จนกว่าเธรดจะออก รหัสต่อไปนี้แสดงให้เห็นถึงการใช้การเข้าร่วม
3. ปัญหาด้านความปลอดภัยแบบมัลติเธรด
สาเหตุของปัญหา: เมื่อมีคำสั่งหลายรายการที่ทำงานในเธรดเดียวกันเพื่อแชร์ข้อมูลหนึ่งเธรดหนึ่งจะดำเนินการส่วนหนึ่งของหลายข้อความ แต่ยังไม่ได้ดำเนินการเสร็จสิ้นและเธรดอื่นมีส่วนร่วมในการดำเนินการส่งผลให้เกิดข้อผิดพลาดในการแชร์ข้อมูล
วิธีแก้ปัญหา: สำหรับหลายข้อความที่แบ่งปันข้อมูลที่มีการดำเนินการหลายรายการสามารถดำเนินการเธรดได้เพียงหนึ่งเธรด ในระหว่างกระบวนการดำเนินการเธรดอื่น ๆ จะไม่ดำเนินการ
ซิงโครไนซ์บล็อกโค้ด:
ชั้นเรียนสาธารณะ ThreadDemo3 {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {ตั๋ว t = ตั๋วใหม่ (); เธรด t1 = เธรดใหม่ (t, "หน้าต่างหนึ่ง"); เธรด t2 = เธรดใหม่ (t, "หน้าต่างสอง"); เธรด t3 = เธรดใหม่ (t, "หน้าต่างสาม"); เธรด t4 = เธรดใหม่ (t, "หน้าต่างสี่"); t1.start (); t2.start (); t3.start (); t4.start (); }} ตั๋วคลาสใช้งาน Runnable {Private Int Ticket = 400; โมฆะสาธารณะเรียกใช้ () {ในขณะที่ (จริง) {ซิงโครไนซ์ (วัตถุใหม่ ()) {ลอง {thread.sleep (1); } catch (interruptedException e) {// toDo บล็อก catch block ที่สร้างขึ้นอัตโนมัติ E.PrintStackTrace (); } ถ้า (ตั๋ว <= 0) หยุด; System.out.println (thread.currentthread (). getName ()+"--- ขาย"+ตั๋ว-); -ฟังก์ชั่นซิงโครนัส
ชั้นเรียนสาธารณะ ThreadDemo3 {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {ตั๋ว t = ตั๋วใหม่ (); เธรด t1 = เธรดใหม่ (t, "หน้าต่างหนึ่ง"); เธรด t2 = เธรดใหม่ (t, "หน้าต่างสอง"); เธรด t3 = เธรดใหม่ (t, "หน้าต่างสาม"); เธรด t4 = เธรดใหม่ (t, "หน้าต่างสี่"); t1.start (); t2.start (); t3.start (); t4.start (); }} ตั๋วคลาสใช้งาน Runnable {Private Int Ticket = 4000; Void Saleticket () {ถ้า (ตั๋ว> 0) System.out.println (thread.currentthread (). getName ()+"ขาย"+ตั๋ว-); } โมฆะสาธารณะเรียกใช้ () {ในขณะที่ (จริง) {Saleticket (); -การล็อคฟังก์ชั่นแบบซิงโครนัสคือการล็อคฟังก์ชั่นการซิงโครไนซ์แบบคงที่นี้คือคลาส
การสื่อสารระหว่างเธรด
คลาสสาธารณะ ThreadDemo3 {โมฆะคงที่สาธารณะหลัก (String [] args) {คลาสบุคคล {ชื่อสตริงสาธารณะ; เพศสตริงส่วนตัว ชุดโมฆะสาธารณะ (ชื่อสตริง, เพศสตริง) {this.name = name; this.gender = เพศ; } โมฆะสาธารณะ get () {system.out.println (this.name+".... "+this.gender); }} คนสุดท้าย p = บุคคลใหม่ (); เธรดใหม่ (ใหม่ runnable () {public void run () {int x = 0; ในขณะที่ (จริง) {ถ้า (x == 0) {p.set ("จางซาน", "ชาย");} else {p.set ("lili", "nv");} x = (x+1)%2; เธรดใหม่ (ใหม่ runnable () {public void run () {ในขณะที่ (จริง) {p.get ();}}}) เริ่มต้น (); }}/ *จางซาน .... ชายจางซาน .... ชาย Lili .... nvlili .... ชายจางซาน .... nvlili .... ชาย *//แก้ไขรหัสด้านบน
คลาสสาธารณะ ThreadDemo3 {โมฆะคงที่สาธารณะหลัก (String [] args) {คลาสบุคคล {ชื่อสตริงสาธารณะ; เพศสตริงส่วนตัว ชุดโมฆะสาธารณะ (ชื่อสตริง, เพศสตริง) {this.name = name; this.gender = เพศ; } โมฆะสาธารณะ get () {system.out.println (this.name+".... "+this.gender); }} คนสุดท้าย p = บุคคลใหม่ (); เธรดใหม่ (ใหม่ runnable () {public void run () {int x = 0; ในขณะที่ (จริง) {ซิงโครไนซ์ (p) {ถ้า (x == 0) {p.set ("จางซาน", "ชาย");} else {p.set ("lili", "nv"); เธรดใหม่ (ใหม่ runnable () {public void run () {ในขณะที่ (true) {ซิงโครไนซ์ (p) {p.get ();}}}}}}) เริ่มต้น (); }} /* lili .... nv lili .... nv lili .... nv lili .... nv lili .... nv lili .... nv lili .... nv lili .... nv lili .... nv lili .... nv zhang san .... ชาย zhang san ซาน .... ชายจางซาน .... ชาย *//กำลังรอกลไกการปลุก
/**กลไกการรอการปลุกของเธรด*การรอและปลุกต้องเป็นล็อคเดียวกัน*/คลาสสาธารณะ ThreadDemo3 {ธงบูลีนแบบคงที่ส่วนตัว = FALSE; โมฆะคงที่สาธารณะหลัก (String [] args) {คลาสบุคคล {ชื่อสตริงสาธารณะ; เพศสตริงส่วนตัว ชุดโมฆะสาธารณะ (ชื่อสตริง, เพศสตริง) {this.name = name; this.gender = เพศ; } โมฆะสาธารณะ get () {system.out.println (this.name+".... "+this.gender); }} คนสุดท้าย p = บุคคลใหม่ (); เธรดใหม่ (ใหม่ runnable () {public void run () {int x = 0; ในขณะที่ (จริง) {ซิงโครไนซ์ (p) {ถ้า (ธง) ลอง {p.wait ();} catch (interruptedException e) {// // toDo } else {p.set ("lili", "nv");} x = (x+1)%2; เธรดใหม่ (ใหม่ runnable () {public void run () {ในขณะที่ (จริง) {ซิงโครไนซ์ (p) {if (! ธง) ลอง {p.wait ();} catch (interruptedException e) {// toDo อัตโนมัติ catchlogtrace }).เริ่ม(); -กลไกการผลิตและการบริโภคหนึ่ง
ชั้นเรียนสาธารณะ ThreadDemo4 {ธงบูลีนแบบคงที่ส่วนตัว = เท็จ; โมฆะคงที่สาธารณะหลัก (สตริง [] args) {สินค้าชั้น {ชื่อสตริงส่วนตัว; INT NUM ส่วนตัว; โมฆะที่ซิงโครไนซ์สาธารณะการผลิต (ชื่อสตริง) {ถ้า (ธง) ลอง {wait (); } catch (interruptedException e) {// toDo บล็อก catch block ที่สร้างขึ้นอัตโนมัติ E.PrintStackTrace (); } this.name = name+"number:"+num ++; System.out.println ("ผลิต ...... "+this.name); ธง = จริง; แจ้งเตือน (); } การบริโภคโมฆะแบบซิงโครไนซ์สาธารณะ () {ถ้า (! ธง) ลอง {รอ (); } catch (interruptedException e) {// toDo บล็อก catch block ที่สร้างขึ้นอัตโนมัติ E.PrintStackTrace (); } system.out.println ("บริโภค *****"+ชื่อ); Flags = FALSE; แจ้งเตือน (); }} สินค้าสุดท้าย g = สินค้าใหม่ (); เธรดใหม่ (ใหม่ runnable () {public void run () {ในขณะที่ (จริง) {g.produce ("ผลิตภัณฑ์");}}}) เริ่มต้น (); เธรดใหม่ (ใหม่ runnable () {public void run () {ในขณะที่ (true) {g.consume ();}}}) เริ่มต้น (); -กลไกการผลิตและการบริโภค 2
ชั้นเรียนสาธารณะ ThreadDemo4 {ธงบูลีนแบบคงที่ส่วนตัว = เท็จ; โมฆะคงที่สาธารณะหลัก (สตริง [] args) {สินค้าชั้น {ชื่อสตริงส่วนตัว; INT NUM ส่วนตัว; โมฆะที่ซิงโครไนซ์สาธารณะผลิต (ชื่อสตริง) {ในขณะที่ (ธง) ลอง {รอ (); } catch (interruptedException e) {// toDo บล็อก catch block ที่สร้างขึ้นอัตโนมัติ E.PrintStackTrace (); } this.name = name+"number:"+num ++; System.out.println (thread.currentthread (). getName ()+"ผลิต ... "+this.name); ธง = จริง; แจ้งเตือน (); } โมฆะที่ซิงโครไนซ์สาธารณะใช้ () {ในขณะที่ (! ธง) ลอง {รอ (); } catch (interruptedException e) {// toDo บล็อก catch block ที่สร้างขึ้นอัตโนมัติ E.PrintStackTrace (); } system.out.println (thread.currentthread (). getName ()+"บริโภค *******"+ชื่อ); Flags = FALSE; แจ้งเตือน (); }} สินค้าสุดท้าย g = สินค้าใหม่ (); เธรดใหม่ (ใหม่ runnable () {public void run () {ในขณะที่ (true) {g.produce ("product");}}}, "ผลิต (" ผลิตภัณฑ์ ");}}}," ผลิต ("ผลิตภัณฑ์");}}}, "ผลิต (" ผลิตภัณฑ์ ");}}} }, "ผู้บริโภคหมายเลข 1"). start (); เธรดใหม่ (ใหม่ runnable () {public void run () {ในขณะที่ (จริง) {g.consume ();}}}, "ผู้บริโภคหมายเลข 2") บริโภค ******* หมายเลขสินค้า: 48050 ผู้ผลิตหมายเลข 1 ผลิต ... หมายเลขสินค้า: 48051Consumer หมายเลข 2 บริโภค **** หมายเลข Commodity: 48051 Producer หมายเลข 2 ผลิต ... หมายเลขสินค้า: 48052Consumer หมายเลข 2 48053Consumer หมายเลข 1 ถูกบริโภค ******* หมายเลขสินค้า: 48053 ผู้ผลิตหมายเลข 1 ได้รับการผลิต ... หมายเลขสินค้า: 48054Consumer หมายเลข 2 ได้ถูกบริโภค ******* จำนวนสินค้า: 48054 48055*/ข้างต้นคือการรวบรวมข้อมูลมัลติเธรด Java เราจะเพิ่มความรู้ที่เกี่ยวข้องต่อไปในอนาคต ขอบคุณสำหรับการสนับสนุนเว็บไซต์นี้!