เธรดเริ่ม:
1. คำอธิบายของความแตกต่างระหว่าง start () และ run ()
เริ่มต้น (): ฟังก์ชั่นของมันคือการเริ่มเธรดใหม่และเธรดใหม่จะเรียกใช้วิธีการเรียกใช้ () ที่สอดคล้องกัน เริ่มต้น () ไม่สามารถเรียกได้ซ้ำ ๆ
Run (): Run () สามารถเรียกได้ซ้ำ ๆ เช่นเดียวกับวิธีการสมาชิกทั่วไป หากคุณเรียกเรียกใช้ () แยกต่างหากให้เรียกใช้ () จะถูกดำเนินการในเธรดปัจจุบันและเธรดใหม่จะไม่เริ่มต้น!
ต่อไปนี้เป็นรหัสที่จะอธิบาย
คลาส MyThread ขยายเธรด {public void run () {... }}; MyThread MyThread = new MyThread (); mythread.start () เริ่มเธรดใหม่และเรียกใช้วิธีการเรียกใช้ () ในเธรดใหม่
mythread.run () จะเรียกใช้วิธีการเรียกใช้ () โดยตรงในเธรดปัจจุบันและจะไม่เริ่มเธรดใหม่เพื่อเรียกใช้ Run ()
2. ตัวอย่างของความแตกต่างระหว่าง start () และ run ()
ด้านล่างแสดงให้เห็นถึงความแตกต่างระหว่างพวกเขาด้วยตัวอย่างง่ายๆ ซอร์สโค้ดมีดังนี้:
// demo.java คลาสซอร์สโค้ดคลาส MyThread ขยายเธรด {Public MyThread (ชื่อสตริง) {Super (ชื่อ); } โมฆะสาธารณะเรียกใช้ () {system.out.println (thread.currentthread (). getName ()+"กำลังทำงานอยู่"); }}; การสาธิตคลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {เธรด mythread = new mythread ("Mythread"); System.out.println (thread.currentthread (). getName ()+"โทรหา mythread.run ()"); Mythread.run (); System.out.println (thread.currentthread (). getName ()+"โทรหา mythread.start ()"); mythread.start (); - ผลการทำงาน:
Main Call Mythread.run () Main คือ RunningMain Call MyThread.start () MyThread กำลังทำงานอยู่
ผลลัพธ์คำอธิบาย:
(1) tread.currentthread (). getName () ใช้เพื่อรับชื่อของ "เธรดปัจจุบัน" เธรดปัจจุบันหมายถึงเธรดที่กำหนดให้ดำเนินการใน CPU
(2) Mythread.run () เรียกว่าใน "Main Thread Main" และวิธีการเรียกใช้ () ทำงานโดยตรงบน "Main Thread Main"
(3) Mythread.start () จะเริ่ม "เธรด MyThread" หลังจาก "เธรด MyThread" เริ่มต้นวิธีการเรียกใช้ () จะถูกเรียก; ในเวลานี้วิธีการเรียกใช้ () ทำงานบน "เธรด MyThread"
เธรดขัดจังหวะและการยุติ
1. การขัดจังหวะเธรด: ขัดจังหวะ ()
ฟังก์ชั่นของ interrupt () คือการขัดจังหวะเธรด
เธรดนี้ได้รับอนุญาตให้ขัดจังหวะตัวเอง เมื่อเธรดอื่นเรียกวิธีการขัดจังหวะ () ของเธรดนี้การอนุญาตจะถูกตรวจสอบผ่าน checkAccess () สิ่งนี้อาจทำให้เกิดข้อยกเว้น SecurityException
หากเธรดอยู่ในสถานะการบล็อก: การโทรรอ () รอ (ยาว) หรือรอ (ยาว, int) ของเธรดจะทำให้มันเป็นสถานะรอ (บล็อก) หรือการโทรเข้าร่วม (), เข้าร่วม (ยาว), เข้าร่วม (ยาว, int), นอนหลับ (ยาว), นอนหลับ (ยาว, int) ของเธรด หากเธรดเรียกวิธีการขัดจังหวะ () เมื่อปิดกั้น "สถานะขัดจังหวะ" ของมันจะถูกล้างและจะได้รับการขัดจังหวะการรับรู้ ตัวอย่างเช่นเธรดเข้าสู่สถานะการบล็อกผ่านการรอ () และเธรดจะถูกขัดจังหวะโดย interrupt (); การเรียก interrupt () จะตั้งค่าธงขัดจังหวะของเธรดให้เป็น "จริง" ทันที แต่เนื่องจากเธรดอยู่ในสถานะการบล็อก "ธงขัดจังหวะ" จะถูกล้างออกเป็น "เท็จ" ทันทีและจะสร้างการขัดจังหวะ
หากเธรดถูกบล็อกในตัวเลือกตัวเลือกแล้วเมื่อมันถูกขัดจังหวะโดย interrupt (); ธงขัดจังหวะของเธรดถูกตั้งค่าเป็น TRUE และจะถูกส่งคืนทันทีจากการเลือกการเลือก
หากไม่ได้เป็นของสถานการณ์ที่กล่าวถึงข้างต้นเมื่อเธรดถูกขัดจังหวะผ่าน interrupt () ธงขัดจังหวะของมันจะถูกตั้งค่าเป็น "จริง"
การขัดจังหวะ "เธรดที่สิ้นสุด" จะไม่ทำให้เกิดการดำเนินการใด ๆ
2. การยกเลิกเธรด
แนะนำให้ใช้วิธีการหยุด () และ suspend () ในเธรดไม่ควรใช้เนื่องจากความไม่มั่นคงโดยธรรมชาติ!
ต่อไปฉันจะหารือเกี่ยวกับวิธีการเลิกจ้างของเธรดใน "การปิดกั้นสถานะ" และ "การเรียกใช้สถานะ" ตามลำดับจากนั้นสรุปวิธีการทั่วไป
1. ยุติเธรดใน "สถานะการปิดกั้น"
โดยปกติเราจะยุติเธรดใน "สถานะการปิดกั้น" โดย "ขัดจังหวะ"
เมื่อเธรดเข้าสู่สถานะการบล็อกเนื่องจากถูกเรียกว่า sleep () ให้รอ (), เข้าร่วม () และวิธีการอื่น ๆ ; หากการขัดจังหวะ () ของเธรดถูกเรียกในเวลานี้เครื่องหมายขัดจังหวะของเธรดจะถูกตั้งค่าเป็น TRUE เนื่องจากอยู่ในสถานะการบล็อกธงขัดจังหวะจะถูกล้างและมีการสร้างข้อยกเว้น InterruptedException การใส่ interruptedException จนกว่าจะเหมาะสมสามารถยกเลิกเธรดในรูปแบบดังนี้:
@overridepublic เป็นโมฆะ Run () {ลอง {ในขณะที่ (จริง) {// ดำเนินการงาน ... }} catch (interruptedException IE) {// เนื่องจากข้อยกเว้น interruptedException ออกจากขณะที่ (จริง) ลูปและเธรดสิ้นสุด! - หมายเหตุ: เมื่องานถูกดำเนินการอย่างต่อเนื่องในขณะที่ (จริง) เมื่อเธรดอยู่ในสถานะการปิดกั้นการเรียกให้ขัดจังหวะ () ของเธรดจะสร้างการขัดจังหวะ interruptedException การจับภาพที่ถูกขัดจังหวะอยู่ข้างนอกในขณะที่ (จริง) ดังนั้นจึงออกจากการวนซ้ำ (จริง) ในขณะที่!
หมายเหตุ: การจับภาพการขัดจังหวะการรับรู้มักจะอยู่ด้านนอกร่างกายของลูป (จริง) ในขณะที่ลูป (จริง) ในขณะที่มีการสร้างข้อยกเว้น มิฉะนั้นหาก InterruptedException อยู่ภายในร่างกายของลูป (จริง) ในขณะที่จำเป็นต้องมีการเพิ่มเพิ่มเติมและการประมวลผลทางออก แบบฟอร์มมีดังนี้:
@OverridePublic เป็นโมฆะ Run () {ในขณะที่ (จริง) {ลอง {// ดำเนินการงาน ... } catch (interruptedException IE) {// interruptedException อยู่ในช่วงเวลาที่แท้จริง (จริง) // เมื่อเธรดสร้างข้อยกเว้น InterruptedException ในขณะที่ (จริง) สามารถทำงานต่อไปได้! จำเป็นต้องออกจากการหยุดพักด้วยตนเอง - หมายเหตุ: ข้อยกเว้น interruptedException ข้างต้นถูกจับภายใน whle (จริง) เมื่อมีการสร้างข้อยกเว้น InterruptedException จะไม่ได้รับการจัดการโดยการจับและยังอยู่ในขณะที่ร่างกายวน (จริง) ในการออกจากร่างกายลูป (จริง) ในขณะที่จำเป็นต้องดำเนินการเพิ่มเติมในขณะที่ (จริง) ต้องดำเนินการ
2. ยุติเธรดใน "สถานะการรัน"
โดยปกติแล้วเราจะยุติเธรดใน "สถานะการทำงาน" โดย "การทำเครื่องหมาย" ในหมู่พวกเขามี "Flag ขัดจังหวะ" และ "เพิ่มธงเพิ่มเติม"
(1) ยุติเธรดผ่าน "Flag ขัดจังหวะ"
แบบฟอร์มมีดังนี้:
@overridepublic เป็นโมฆะ Run () {ในขณะที่ (! isInterrupted ()) {// งานเรียกใช้งาน ... }} คำอธิบาย: isinterrupted () กำหนดว่าการตั้งค่าสถานะอินเตอร์รัปต์ของเธรดเป็นจริงหรือไม่ เมื่อเธรดอยู่ในสถานะการทำงานและเราจำเป็นต้องยุติมัน วิธีการขัดจังหวะของเธรด () สามารถเรียกได้และธงขัดจังหวะของเธรดนั้นถูกทำเครื่องหมายไว้จริงนั่นคือ Isinterrupted () จะกลับมาเป็นจริง ณ จุดนี้จะมีการวนซ้ำในขณะที่จะออก
หมายเหตุ: interrupt () ไม่ยุติเธรดใน "การเรียกใช้สถานะ"! มันตั้งค่าธงขัดจังหวะของเธรดให้เป็นจริง
(2) ผ่าน "เพิ่มเครื่องหมายเพิ่มเติม"
แบบฟอร์มมีดังนี้:
Flag บูลีนผันผวนส่วนตัว = true; void stoptask () {flag = false;}@overridepublic void run () {ในขณะที่ (ธง) {// ดำเนินการงาน ... }}} คำอธิบาย: มีแท็กแฟล็กในเธรดและค่าเริ่มต้นเป็นจริง และเราให้ StopTask () เพื่อตั้งค่าแท็กแฟล็ก เมื่อเราจำเป็นต้องยกเลิกเธรดการเรียกใช้วิธี stoptask () ของเธรดสามารถทำให้เธรดออกจากการวนซ้ำในขณะที่
หมายเหตุ: ธงถูกกำหนดเป็นประเภทที่ผันผวนเพื่อให้แน่ใจว่าการมองเห็นของธง นั่นคือหลังจากเธรดอื่น ๆ แก้ไขการตั้งค่าสถานะผ่าน StopTask () เธรดนี้สามารถดูค่าธงที่แก้ไขได้
ครอบคลุมวิธีการเลิกจ้างของเธรดใน "การปิดกั้นสถานะ" และ "การเรียกใช้สถานะ" เธรดการเลิกจ้างทั่วไปมีดังนี้:
@OverridePublic เป็นโมฆะ Run () {ลอง {// 1. isInterrupted () รับประกันว่าเธรดจะถูกยกเลิกตราบใดที่การขัดจังหวะถูกทำเครื่องหมายจริง ในขณะที่ (! isInterrupted ()) {// ดำเนินการงาน ... }} catch (interruptedException IE) {// 2. InterruptedException Exception รับประกันว่าเมื่อมีข้อยกเว้น interruptedException เกิดขึ้นเธรดจะถูกยกเลิก - 3. ตัวอย่างของการยกเลิกเธรด
interrupt () มักจะใช้เพื่อยุติเธรด "การปิดกั้นสถานะ" อ้างถึงตัวอย่างต่อไปนี้:
// demo1.java คลาสซอร์สโค้ดคลาส MyThread ขยายเธรด {Public MyThread (ชื่อสตริง) {Super (ชื่อ); } @Override โมฆะสาธารณะเรียกใช้ () {ลอง {int i = 0; ในขณะที่ (! isInterrupted ()) {thread.sleep (100); // นอนหลับ 100ms i ++; System.out.println (thread.currentthread (). getName ()+"("+this.getState ()+") loop"+i); }} catch (interruptedException e) {system.out.println (thread.currentthread (). getName ()+"("+this.getState ()+") catch interruptedException"); }}} คลาสสาธารณะ demo1 {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {ลอง {เธรด t1 = ใหม่ mythread ("t1"); // สร้าง "เธรด T1" ใหม่ System.out.println (t1.getName ()+"("+t1.getState ()+") เป็นของใหม่"); t1.start (); // เริ่ม "เธรด t1" system.out.println (t1.getName ()+"("+t1.getState ()+") เริ่มต้นขึ้น"); // เธรดหลักจะนอน 300ms จากนั้นเธรดหลักจะส่งคำสั่ง "ขัดจังหวะ" ไปยัง T1 Thread.sleep (300); t1.interrupt (); System.out.println (t1.getName ()+"("+t1.getState ()+") ถูกขัดจังหวะ"); // เธรดหลักจะนอน 300ms แล้วตรวจสอบสถานะของ T1 Thread.sleep (300); System.out.println (t1.getName ()+"("+t1.getState ()+") ถูกขัดจังหวะตอนนี้"); } catch (interruptedException e) {e.printStackTrace (); - ผลการทำงาน:
T1 (ใหม่) คือ new.t1 (runnable) เริ่มต้น T1 (เรียกใช้) ลูป 1T1 (เรียกใช้) ลูป 2T1 (timed_waiting) ถูกขัดจังหวะ T1
ผลลัพธ์คำอธิบาย:
(1) เธรดหลักหลักสร้างเธรด T1 ผ่าน New MyThread ("T1") จากนั้นเริ่มเธรด T1 ถึง T1.Start ()
(2) หลังจากเริ่ม T1 แล้วธงขัดจังหวะจะถูกตรวจสอบอย่างต่อเนื่อง หากธงขัดจังหวะเป็น "เท็จ" มันจะนอนหลับ 100ms
(3) หลังจากนอนหลับ T1 มันจะเปลี่ยนไปเป็นเธรดหลักหลัก เมื่อเธรดหลักทำงานอีกครั้ง t1.interrupt () จะถูกดำเนินการเพื่อขัดจังหวะเธรด t1 หลังจาก T1 ได้รับคำสั่งขัดจังหวะธงขัดจังหวะของ T1 จะถูกตั้งค่าเป็น "เท็จ" และการขัดจังหวะการรับรู้จะถูกโยนลงไป ในวิธีการเรียกใช้ () ของ T1 มันเป็นข้อยกเว้นที่จับอยู่นอกร่างกายลูปในขณะที่; ดังนั้นลูปจึงถูกยกเลิก
เราทำการดัดแปลงเล็กน้อยเพื่อผลลัพธ์ข้างต้นและย้ายบล็อกรหัสที่จับข้อยกเว้น interruptedException ในวิธีการเรียกใช้ () ไปยังร่างกายลูปในขณะที่
// demo2.java คลาสซอร์สโค้ดคลาส MyThread ขยายเธรด {Public MyThread (ชื่อสตริง) {Super (ชื่อ); } @Override โมฆะสาธารณะ Run () {int i = 0; ในขณะที่ (! isInterrupted ()) {ลอง {thread.sleep (100); // sleep สำหรับ 100ms} catch (interruptedException ie) {system.out.println (thread.currentthread (). getName ()+"("+this.getState ()+") จับ interruptedException."); } i ++; System.out.println (thread.currentthread (). getName ()+"("+this.getState ()+") loop"+i); }}} คลาสสาธารณะ demo2 {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {ลอง {เธรด t1 = ใหม่ mythread ("t1"); // สร้าง "เธรด T1" ใหม่ System.out.println (t1.getName ()+"("+t1.getState ()+") เป็นของใหม่"); t1.start (); // เริ่ม "เธรด t1" system.out.println (t1.getName ()+"("+t1.getState ()+") เริ่มต้นขึ้น"); // เธรดหลักจะนอน 300ms จากนั้นเธรดหลักจะส่งคำสั่ง "ขัดจังหวะ" ไปยัง T1 Thread.sleep (300); t1.interrupt (); System.out.println (t1.getName ()+"("+t1.getState ()+") ถูกขัดจังหวะ"); // เธรดหลักจะนอน 300ms แล้วตรวจสอบสถานะของ T1 Thread.sleep (300); System.out.println (t1.getName ()+"("+t1.getState ()+") ถูกขัดจังหวะตอนนี้"); } catch (interruptedException e) {e.printStackTrace (); - ผลการทำงาน:
T1 (ใหม่) คือ new.t1 (runnable) เริ่มต้น T1 (เรียกใช้) ลูป 1T1 (เรียกใช้) วนซ้ำ 2T1 (timed_waiting) ถูกขัดจังหวะ T1 (เรียกใช้) interruptedException.t1 (Runnable) Loop 3T1 (runnable) loop 6T1 (runnable) loop 7T1 (runnable) loop 8T1 (runnable) loop 9 ...
ผลลัพธ์คำอธิบาย:
โปรแกรมได้เข้าสู่วงที่ตายแล้ว!
ทำไมสิ่งนี้ถึงเกิดขึ้น? นี่เป็นเพราะ T1 ถูกขัดจังหวะโดย interrupt () เมื่อมัน "รอ (การปิดกั้น) สถานะ" ในเวลานี้ธงขัดจังหวะจะถูกล้าง [ที่ถูกขัดจังหวะ () จะกลับมาเป็นเท็จ] และข้อยกเว้น interruptedException ถูกโยน [ข้อยกเว้นนี้ถูกจับได้ในขณะที่วงวนอยู่] ดังนั้น T1 จะเข้าสู่วงจรอุบาทว์ตามธรรมชาติ
เพื่อแก้ปัญหานี้เราจำเป็นต้องประมวลผลเพิ่มเติมเพื่อออกจากการวนซ้ำเมื่อจับข้อยกเว้น ตัวอย่างเช่นการเพิ่มการหยุดพักหรือกลับไปที่ MyThread Catch (InterruptedException) สามารถแก้ปัญหาได้
นี่คือตัวอย่างของเธรดที่ยุติ "สถานะสถานะ" โดย "เพิ่มแท็กเพิ่มเติม":
// demo3. Java คลาสซอร์สโค้ดคลาส MyThread ขยายเธรด {ธงบูลีนผันผวนส่วนตัว = true; โมฆะสาธารณะ stoptask () {flag = false; } Public MyThread (ชื่อสตริง) {super (ชื่อ); } @Override โมฆะสาธารณะเรียกใช้ () {ซิงโครไนซ์ (นี่) {ลอง {int i = 0; ในขณะที่ (ธง) {thread.sleep (100); // นอนหลับ 100ms i ++; System.out.println (thread.currentthread (). getName ()+"("+this.getState ()+") loop"+i); }} catch (interruptedException ie) {system.out.println (thread.currentthread (). getName ()+"("+this.getState ()+") จับ interruptedException"); }}}} คลาสสาธารณะ demo3 {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {ลอง {mythread t1 = ใหม่ mythread ("t1"); // สร้าง "เธรด T1" ใหม่ System.out.println (t1.getName ())+"("+t1.getState ()+") เป็นของใหม่"); t1.start (); // เริ่ม "เธรด t1" system.out.println (t1.getName ()+"("+t1.getState ()+") เริ่มต้นขึ้น"); // เธรดหลักจะนอน 300ms จากนั้นเธรดหลักจะส่งคำสั่ง "ขัดจังหวะ" ไปยัง T1 Thread.sleep (300); t1.stoptask (); System.out.println (t1.getName ()+"("+t1.getState ()+") ถูกขัดจังหวะ"); // เธรดหลักจะนอน 300ms แล้วตรวจสอบสถานะของ T1 Thread.sleep (300); System.out.println (t1.getName ()+"("+t1.getState ()+") ถูกขัดจังหวะตอนนี้"); } catch (interruptedException e) {e.printStackTrace (); - ผลการทำงาน:
T1 (ใหม่) คือ new.t1 (runnable) เริ่มต้น T1 (เรียกใช้) ลูป 1T1 (เรียกใช้) ลูป 2T1 (timed_waiting) ถูกขัดจังหวะ T1 (เรียกใช้) ลูป 3T1 (สิ้นสุด) ถูกขัดจังหวะตอนนี้