แผนภาพสถานะเธรด
เธรดรวมถึง 5 สถานะต่อไปนี้
1. สถานะใหม่: หลังจากสร้างวัตถุเธรดแล้วมันจะเข้าสู่สถานะใหม่ ตัวอย่างเช่นเธรดเธรด = เธรดใหม่ ()
2. Runnable: ยังเป็นที่รู้จักกันในชื่อ "สถานะการดำเนินการ" หลังจากสร้างวัตถุเธรดเธรดอื่น ๆ เรียกเมธอดเริ่มต้น () ของวัตถุเพื่อเริ่มเธรด ตัวอย่างเช่น thread.start () เธรดในสถานะพร้อมอาจถูกกำหนดให้ดำเนินการโดย CPU ได้ตลอดเวลา
3. สถานะการรัน (กำลังทำงาน): เธรดได้รับสิทธิ์ CPU สำหรับการดำเนินการ ควรสังเกตว่าเธรดสามารถป้อนสถานะการทำงานจากสถานะพร้อมเท่านั้น
4. สถานะที่ถูกบล็อก: สถานะที่ถูกบล็อกหมายความว่าเธรดจะให้สิทธิ์การใช้งาน CPU ด้วยเหตุผลบางอย่างและหยุดทำงานชั่วคราว มันไม่ได้จนกว่าเธรดจะเข้าสู่สถานะพร้อมที่จะมีโอกาสไปที่สถานะการวิ่ง การอุดตันมีสามประเภท:
(01) รอบล็อก - โดยเรียกเมธอดการรอของเธรด () ให้เธรดรอให้เสร็จงานบางอย่าง
(02) การปิดกั้นแบบซิงโครไนซ์-เธรดไม่สามารถรับการล็อคซิงโครไนซ์ซิงโครไนซ์ (เนื่องจากล็อคถูกครอบครองโดยเธรดอื่น ๆ ) มันจะเข้าสู่สถานะการปิดกั้นแบบซิงโครไนซ์
(03) การปิดกั้นอื่น ๆ-เธรดจะเข้าสู่สถานะการบล็อกโดยเรียก Sleep () หรือเข้าร่วม () ของเธรดหรือออกคำขอ I/O เมื่อสถานะการนอนหลับ () หมดเวลาเข้าร่วม () รอให้เธรดยุติหรือหมดเวลาหรือการประมวลผล I/O เสร็จสิ้นเธรดจะกลับเข้าสู่สถานะพร้อมอีกครั้ง
5. สถานะ Dead: เธรดเสร็จสิ้นการดำเนินการหรือออกจากวิธีการเรียกใช้ () เนื่องจากข้อยกเว้นและเธรดจะสิ้นสุดวัฏจักรชีวิต
ใช้เธรดวิธีการแบบมัลติเธรดและ runnable
เธรด: สืบทอดคลาสเธรดใช้วิธีการเรียกใช้และเรียกใช้วิธีการเริ่มต้นในฟังก์ชันหลักเพื่อเริ่มเธรด
Runnable: อินเทอร์เฟซใช้อินเตอร์เฟสที่รันได้ส่งผ่านเป็นพารามิเตอร์ไปยังตัวสร้างเธรดและเรียกวิธีการเริ่ม
ตัวอย่าง:
งานคลาสใช้งาน Runnable {ตั๋ว INT ส่วนตัว = 10; @Override โมฆะสาธารณะ Run () {สำหรับ (int i = 0; i <20; i ++) {ถ้า (this.ticket> 0) {system.out.println (thread.currentthread (). getName () + "ขายตั๋ว" + this.ticket-); - คลาสสาธารณะ runnableTest {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {task mytask = task ใหม่ (); เธรด t1 = เธรดใหม่ (mytask); เธรด t2 = เธรดใหม่ (mytask); เธรด t3 = เธรดใหม่ (mytask); t1.start (); t2.start (); t3.start (); }} // threadtest.java คลาสซอร์สโค้ดคลาส MyThread ขยายเธรด {ตั๋ว int ส่วนตัว = 10; โมฆะสาธารณะเรียกใช้ () {สำหรับ (int i = 0; i <20; i ++) {ถ้า (this.ticket> 0) {system.out.println (this.getName () + "ขายตั๋ว: ตั๋ว" + this.ticket--); }}}} คลาสสาธารณะ Threadtest {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {// เริ่ม 3 เธรด t1, t2, t3; แต่ละเธรดขายตั๋ว 10 ใบแต่ละใบ! MYTHREAD T1 = ใหม่ MYTHREAD (); MYTHREAD T2 = ใหม่ MYTHREAD (); MYTHREAD T3 = ใหม่ MYTHREAD (); t1.start (); t2.start (); t3.start (); -
ความแตกต่างระหว่างเธรดและ runnable
เธรดเป็นคลาสและ Runnable เป็นอินเทอร์เฟซ เธรดเองเป็นคลาสที่ใช้อินเทอร์เฟซที่รันได้ เรารู้ว่า "คลาสสามารถมีคลาสพาเรนต์เดียวเท่านั้น แต่สามารถใช้หลายอินเทอร์เฟซ" ดังนั้น Runnable จึงมีความสามารถในการปรับขนาดได้ดีขึ้น นอกจากนี้ยังสามารถใช้งานได้สำหรับ "การแบ่งปันทรัพยากร" นั่นคือหลายเธรดถูกสร้างขึ้นตามวัตถุที่รันได้บางอย่างและพวกเขาจะแบ่งปันทรัพยากรบนวัตถุที่รันได้ โดยทั่วไปขอแนะนำให้ใช้มัลติเธรดผ่าน "runnable"!
การทำงานของเธรดและเริ่มต้น
เริ่มต้น (): ฟังก์ชั่นของมันคือการเริ่มเธรดใหม่และเธรดใหม่จะเรียกใช้วิธีการเรียกใช้ () ที่สอดคล้องกัน เริ่มต้น () ไม่สามารถเรียกได้ซ้ำ ๆ start () จริงเริ่มต้นเธรดผ่านวิธีการท้องถิ่น start0 () start0 () จะเรียกใช้เธรดใหม่และเธรดใหม่จะเรียกวิธีการเรียกใช้ ()
Run (): Run () สามารถเรียกได้ซ้ำ ๆ เช่นเดียวกับวิธีการสมาชิกทั่วไป หากคุณเรียกเรียกใช้ () แยกต่างหากให้เรียกใช้ () จะถูกดำเนินการในเธรดปัจจุบันและเธรดใหม่จะไม่เริ่มต้น! Run () คือการเรียกใช้วิธีการเรียกใช้ () ของสมาชิกที่รันได้ของเธรดเธรดโดยตรงและจะไม่สร้างเธรดใหม่
// demo.java คลาสซอร์สโค้ดคลาส MyThread ขยายเธรด {Public MyThread (ชื่อสตริง) {Super (ชื่อ); } โมฆะสาธารณะเรียกใช้ () {system.out.println (thread.currentthread (). getName ()+"กำลังทำงานอยู่"); - การสาธิตคลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {เธรด MyThread = ใหม่ 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 กำลังทำงานอยู่
ซิงโครไนซ์
ใน Java แต่ละวัตถุมีการล็อคการซิงโครไนซ์ เมื่อเราเรียกวิธีการซิงโครไนซ์ของวัตถุการล็อควัตถุจะได้รับและซิงโครไนซ์ (OBJ) จะได้รับการล็อคการซิงโครไนซ์ของ "OBJ Object" การเข้าถึงเธรดที่แตกต่างกันไปยังล็อคการซิงโครไนซ์นั้นเป็นเอกสิทธิ์เฉพาะบุคคล การล็อคการซิงโครไนซ์ของวัตถุสามารถรับได้โดยหนึ่งเธรดในเวลาหนึ่งเท่านั้น ผ่านการล็อคการซิงโครไนซ์เราสามารถเข้าถึงการเข้าถึง "วัตถุ/วิธี" ซึ่งกันและกันร่วมกันในหลายเธรด ตัวอย่างเช่นขณะนี้มีสองเธรด A และ Thread B ซึ่งทั้งหมดจะเข้าถึง "การล็อคแบบซิงโครไนซ์ของ Object OBJ" สมมติว่าในบางจุดเธรด A ได้มา "ล็อคการซิงโครไนซ์ของ OBJ" และดำเนินการบางอย่าง ในเวลานี้เธรด B ยังพยายามที่จะได้รับ "การเชื่อมโยงการซิงโครไนซ์ของ OBJ" - เธรด B จะล้มเหลวในการรับมันจะต้องรอจนกว่าเธรดจะปล่อย "ล็อคการซิงโครไนซ์ของ OBJ" และสามารถเรียกใช้ได้เท่านั้น
กฎพื้นฐาน
บทความที่ 1: เมื่อเธรดเข้าถึง "วิธีการซิงโครไนซ์" หรือ "บล็อกรหัสที่ซิงโครไนซ์" ของ "วัตถุบางอย่าง" เธรดอื่นจะถูกบล็อกจากการเข้าถึง "วิธีการซิงโครไนซ์" หรือ "บล็อกรหัสที่ซิงโครไนซ์" ของ "วัตถุ"
บทความที่ 2: เมื่อเธรดเข้าถึง "วิธีการซิงโครไนซ์" หรือ "บล็อกรหัสที่ซิงโครไนซ์" ของ "วัตถุบางอย่าง" เธรดอื่น ๆ ยังสามารถเข้าถึงบล็อกรหัสแบบอะซิงโครไนซ์ของ "วัตถุนี้"
ข้อ 3: เมื่อเธรดเข้าถึง "วิธีการซิงโครไนซ์" หรือ "บล็อกรหัสที่ซิงโครไนซ์" ของ "วัตถุบางอย่าง" เธรดอื่นจะถูกบล็อกจากการเข้าถึง "วิธีการซิงโครไนซ์" อื่น ๆ หรือ "บล็อกรหัสที่ซิงโครไนซ์" ของ "วัตถุ"
วิธีการซิงโครไนซ์
Void FOO1 () {System.out.println ("วิธีการซิงโครไนซ์");} รหัสที่ซิงโครไนซ์บล็อกโมฆะสาธารณะ foo2 () {ซิงโครไนซ์ (นี้) {system.out.println ("วิธีการซิงโครไนซ์"); -สิ่งนี้ในบล็อกรหัสที่ซิงโครไนซ์หมายถึงวัตถุปัจจุบัน นอกจากนี้ยังสามารถแทนที่ด้วยวัตถุอื่น ๆ เช่นนี้ถูกแทนที่ด้วย OBJ จากนั้น foo2 () จะได้รับการล็อคการซิงโครไนซ์ของ OBJ เมื่อซิงโครไนซ์ (OBJ)
บล็อกรหัสที่ซิงโครไนซ์สามารถควบคุมพื้นที่การเข้าถึงที่จำกัดความขัดแย้งได้อย่างแม่นยำมากขึ้นและบางครั้งก็มีประสิทธิภาพมากขึ้น
ล็อคอินสแตนซ์และล็อคทั่วโลก
อินสแตนซ์ล็อค-ล็อคบนวัตถุอินสแตนซ์ หากชั้นเรียนเป็นซิงเกิลตันแล้วล็อคก็มีแนวคิดของการล็อคทั่วโลก คำหลักที่ซิงโครไนซ์สอดคล้องกับการล็อคอินสแตนซ์
Global Lock- ล็อคนี้มีเป้าหมายในชั้นเรียน ไม่ว่าอินสแตนซ์จะมีวัตถุกี่ชิ้นเธรดจะแชร์ล็อค ล็อคทั่วโลกสอดคล้องกับการซิงโครไนซ์แบบคงที่ (หรือล็อคบนคลาสหรือวัตถุคลาสโหลดของคลาสนี้)
Pulbic Class บางอย่าง {public synchronized void issynca () {} public synchronized void issyncb () {} public synchronized void csynca () {} โมฆะแบบคงที่แบบคงที่ cyncb () {}}}}}}}}}}}}}
(01) X.ISSYNCA () และ X.ISSYNCB () ไม่สามารถเข้าถึงได้พร้อมกัน เพราะ ISSYNCA () และ ISSYNCB () เป็นล็อคการซิงโครไนซ์ที่เข้าถึงวัตถุเดียวกัน (วัตถุ X)!
(02) x.issynca () และ y.issynca () สามารถเข้าถึงได้ในเวลาเดียวกัน เนื่องจากไม่ได้เข้าถึงการล็อคการซิงโครไนซ์ของวัตถุเดียวกัน x.issynca () เข้าถึงการล็อคการซิงโครไนซ์ของ x ในขณะที่ y.issynca () เข้าถึงล็อคการซิงโครไนซ์ของ y
(03) x.csynca () และ y.csyncb () ไม่สามารถเข้าถึงได้พร้อมกัน เนื่องจาก csynca () และ csyncb () เป็นทั้งประเภทคงที่ x.csynca () เทียบเท่ากับบางสิ่งบางอย่าง issynca () และ y.csyncb () เทียบเท่ากับบางสิ่งบางอย่าง issyncb ()
(04) x.issynca () และบางสิ่งบางอย่าง csynca () สามารถเข้าถึงได้พร้อมกัน เนื่องจาก issynca () เป็นวิธีการอินสแตนซ์, x.issynca () ใช้ล็อคของวัตถุ x; ในขณะที่ csynca () เป็นวิธีการคงที่บางอย่าง csynca () สามารถเข้าใจได้ว่ามันเป็น "ล็อคคลาส" ที่ใช้ ดังนั้นพวกเขาสามารถเข้าถึงได้พร้อมกัน
การปิดกั้นเธรดและการรอปลุกแจ้งเตือนแจ้งเตือน
ใน Object.java อินเทอร์เฟซเช่น WAIT (), Notify () และ NotifyAll () ถูกกำหนดไว้ ฟังก์ชั่นของการรอ () คือการให้เธรดปัจจุบันป้อนสถานะการรอและรอ () จะปล่อยให้เธรดปัจจุบันปล่อยล็อคไว้ บทบาทของการแจ้งเตือน () และแจ้งเตือน () คือการปลุกเธรดรอบนวัตถุปัจจุบัน แจ้งเตือน () คือการปลุกเธรดเดียวในขณะที่ NotifyAll () คือการปลุกเธรดทั้งหมด
รายละเอียด API เกี่ยวกับการรอ/ปลุกในคลาสวัตถุมีดังนี้:
แจ้งเตือน () - ปลุกเธรดเดียวที่รออยู่บนจอภาพวัตถุนี้
NotifyAll () - ปลุกเธรดทั้งหมดที่รอการตรวจสอบวัตถุนี้
Wait () - ใส่เธรดปัจจุบันในสถานะ "รอ (บล็อก)" และ "จนกระทั่งเธรดอื่น ๆ เรียกวิธีการแจ้งเตือน () หรือวิธีการแจ้งเตือน () ของวัตถุนี้" และเธรดปัจจุบันจะถูกปลุก (ป้อนไปยัง "สถานะพร้อม")
รอ (หมดเวลานาน) - ใส่เธรดปัจจุบันใน "การรอ (บล็อก) สถานะ" และ "จนกว่าเธรดอื่นจะเรียกวิธีการแจ้งเตือนของวัตถุ () หรือแจ้งเตือน () หรือเกินกว่าระยะเวลาที่กำหนด"
รอ (การหมดเวลานาน, int nanos) - ให้เธรดปัจจุบันอยู่ใน "สถานะรอ (บล็อก)", "จนกว่าเธรดอื่นจะเรียกวิธีการแจ้งเตือนของวัตถุ () หรือวิธีการแจ้งเตือน () หรือเธรดอื่น ๆ ขัดจังหวะเธรดปัจจุบันหรือเกินระยะเวลาหนึ่ง" และเธรดปัจจุบันถูกปลุก
// waittest.java คลาสซอร์สโค้ดคลาส Threada ขยายเธรด {public threada (ชื่อสตริง) {super (ชื่อ); } โมฆะสาธารณะเรียกใช้ () {ซิงโครไนซ์ (นี่) {system.out.println (thread.currentthread (). getName ()+"การโทรแจ้ง ()"); // ปลุกเธรดรอปัจจุบันแจ้งเตือน (); }}} คลาสสาธารณะ waittest {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {threada t1 = threada ใหม่ ("t1"); ซิงโครไนซ์ (t1) {ลอง {// เริ่มต้น "เธรด t1" system.out.println (thread.currentthread (). getName ()+"เริ่มต้น T1"); t1.start (); // เธรดหลักรอให้ T1 ตื่นขึ้นมาผ่านการแจ้งเตือน () System.out.println (thread.currentthread (). getName ()+"รอ ()"); t1.wait (); System.out.println (thread.currentthread (). getName ()+"ดำเนินการต่อ"); } catch (interruptedException e) {e.printStackTrace (); -เอาท์พุท
เริ่มต้นหลัก t1main wait () t1 การโทรแจ้ง () หลักดำเนินการต่อ
(01) โปรดทราบว่า "เธรดหลัก" ในรูปแสดงถึง "เธรดหลักหลัก" "Thread T1" หมายถึง "Thread T1" เริ่มต้นใน Waittest และ "ล็อค" หมายถึง "การล็อคแบบซิงโครนัสของวัตถุ T1"
(02) "เธรดหลัก" สร้าง "เธรด T1" ใหม่ผ่าน Threada ใหม่ ("T1") จากนั้น "ล็อคแบบซิงโครนัสของวัตถุ T1" จะได้รับผ่านการซิงโครไนซ์ (T1) จากนั้นโทร t1.start () เพื่อเริ่ม "เธรด t1"
(03) "เธรดหลัก" ดำเนินการ t1.wait () เพื่อปล่อย "ล็อคของวัตถุ T1" และเข้าสู่ "รอ (บล็อก) สถานะ" รอเธรดบนวัตถุ T1 เพื่อปลุกผ่านการแจ้งเตือน () หรือแจ้งเตือน ()
(04) หลังจากเรียกใช้ "เธรด T1" แล้ว "ล็อคของวัตถุปัจจุบัน" จะได้รับผ่านการซิงโครไนซ์ (นี่); จากนั้นโทรแจ้งให้ทราบ () เพื่อปลุก "การรอเธรดบนวัตถุปัจจุบัน" นั่นคือตื่นขึ้นมา "เธรดหลัก"
(05) หลังจาก "เธรด T1" เสร็จสิ้นให้ปล่อย "ล็อคของวัตถุปัจจุบัน" หลังจากนั้นทันที "เธรดหลัก" จะได้รับ "ล็อคของวัตถุ T1" จากนั้นเรียกใช้
t1.wait () คือวิธีการรอ () ที่เรียกว่า "เธรด t1" แต่สถานที่ที่ t1.wait () เรียกว่าอยู่ใน "Main Thread Main" เธรดหลักจะต้องเป็น "เธรดปัจจุบัน" นั่นคือสถานะการรันก่อนที่ T1.Wait () สามารถดำเนินการได้ ดังนั้น "เธรดปัจจุบัน" ในเวลานี้คือ "เธรดหลักหลัก"! ดังนั้น t1.wait () คือการทำ "เธรดหลัก" รอไม่ใช่ "เธรด t1"!
แพ็คเกจเธรดการทดสอบ; คลาสสาธารณะ NotifyAllTest {วัตถุคงที่ส่วนตัว obj = วัตถุใหม่ (); โมฆะคงที่สาธารณะหลัก (สตริง [] args) {threada t1 = threada ใหม่ ("t1"); threada t2 = threada ใหม่ ("t2"); threada t3 = threada ใหม่ ("t3"); t1.start (); t2.start (); t3.start (); ลอง {system.out.println (thread.currentthread (). getName ()+"sleep (3000)"); Thread.sleep (3000); } catch (interruptedException e) {e.printStackTrace (); } ซิงโครไนซ์ (obj) {system.out.println (thread.currentthread (). getName ()+"NotifyAll ()"); obj.notifyall (); // ปลุก t1.t2.t3 ที่นี่}} คลาสคงที่เธรดขยายเธรด {public threada (ชื่อสตริง) {super (ชื่อ); } โมฆะสาธารณะเรียกใช้ () {ซิงโครไนซ์ (obj) {ลอง {// printout result system.out.println (thread.currentthread (). getName () + "รอ"); // ปล่อย Obj Object Lock Obj.wait (); // printout result system.out.println (thread.currentthread (). getName () + "ดำเนินการต่อ"); } catch (interruptedException e) {e.printStackTrace (); - เอาท์พุท:
T1 Waitmain Sleep (3000) T3 Waitt2 Waitmain Notifyall () T2 ดำเนินการต่อ 3 ต่อไป
(01) 3 เธรด "T1", "T2" และ "T3" ถูกสร้างขึ้นและเริ่มต้นในหัวข้อหลัก
(02) ด้ายหลักนอนเป็นเวลา 3 วินาทีผ่านการนอนหลับ (3000) ในระหว่างการนอนหลับของเธรดหลักเป็นเวลา 3 วินาทีเราคิดว่าสามเธรด "T1", "T2" และ "T3" ทั้งหมดกำลังทำงานอยู่ ใช้ "T1" เป็นตัวอย่าง เมื่อมันทำงานมันจะดำเนินการ obj.wait () เพื่อรอให้เธรดอื่นตื่นขึ้นมาผ่านการแจ้งเตือน () หรือ nofityall (); ในทำนองเดียวกัน "T2" และ "T3" จะรอให้เธรดอื่นปลุกพวกเขาผ่าน nofity () หรือ nofityall ()
(03) เธรดหลักจะนอนเป็นเวลา 3 วินาทีแล้ววิ่ง ดำเนินการ obj.notifyall () เพื่อปลุกเธรดรอบน OBJ นั่นคือปลุกสามเธรด "T1", "T2" และ "T3" ทันทีหลังจากเรียกใช้งาน Synchronized (OBJ) ของเธรดหลักแล้วเธรดหลักจะปล่อย "OBJ Lock" ด้วยวิธีนี้ "T1", "T2" และ "T3" สามารถรับ "OBJ Lock" และวิ่งต่อไปได้!
แจ้งเตือนและล็อคความสัมพันธ์
ฟังก์ชั่นเช่น WAIT (), แจ้ง () ในวัตถุเช่นซิงโครไนซ์จะทำงานกับ "ล็อคการซิงโครไนซ์วัตถุ"
รอ () จะทำให้ "เธรดปัจจุบัน" รอ เนื่องจากเธรดเข้าสู่สถานะการรอคอยเธรดควรปล่อย "การล็อคแบบซิงโครนัส" ที่ถือโดยล็อคมิฉะนั้นเธรดอื่น ๆ จะไม่สามารถรับ "การล็อคแบบซิงโครนัส" และจะไม่สามารถเรียกใช้ได้!
ตกลงหลังจากการเรียกเธรดรอ () มันจะปล่อย "การล็อคแบบซิงโครนัส" ที่ถือโดยล็อค และจากการแนะนำก่อนหน้านี้เรารู้ว่าสามารถปลุกเธรดที่รอได้โดยแจ้งเตือน () หรือแจ้งเตือน () ตอนนี้โปรดคิดเกี่ยวกับคำถาม: อะไรคือการแจ้งเตือน () ตามการปลุกเธรดที่รอคอย? หรือความสัมพันธ์ระหว่างการรอ () และแจ้ง () คืออะไร? คำตอบคือ: ขึ้นอยู่กับ "ล็อคการซิงโครไนซ์วัตถุ"
เธรดที่รับผิดชอบในการตื่นเธรดรอ (เราเรียกมันว่า "Wake Up Thread") สามารถปลุกเธรดที่รอได้หลังจากได้รับ "การล็อคแบบซิงโครนัสของวัตถุนี้" (การล็อคการซิงโครไนซ์ที่นี่จะต้องเหมือนกับการล็อคการซิงโครไนซ์ของเธรดรอ) แม้ว่ากระทู้ที่รอคอยจะถูกปลุกขึ้นมา อย่างไรก็ตามไม่สามารถดำเนินการได้ทันทีเนื่องจากเธรดปลุกยังคงถือ "การล็อคแบบซิงโครนัสสำหรับวัตถุ" คุณต้องรอจนกว่าเธรดปลุกจะปล่อย "ล็อคการซิงโครไนซ์ของวัตถุ" ก่อนที่คุณจะได้รับ "ล็อคการซิงโครไนซ์ของวัตถุ" และทำงานต่อไป
ในระยะสั้นแจ้ง (), รอ () ขึ้นอยู่กับ "การล็อคแบบซิงโครนัส" ซึ่งจัดขึ้นโดยล็อควัตถุและแต่ละวัตถุมีและเพียงหนึ่งเดียว! นี่คือเหตุผลที่ฟังก์ชั่นเช่น Notify (), Wait () ถูกกำหนดไว้ในคลาส Object ไม่ใช่ในคลาสเธรด
ผลสัมฤทธิ์
เธรดสัมปทานทำให้เธรดเปลี่ยนไปจากสถานะการดำเนินการเป็นสถานะพร้อมเพื่อให้เธรดรออื่น ๆ ที่มีลำดับความสำคัญเดียวกันสามารถรับสิทธิ์การดำเนินการได้ อย่างไรก็ตามไม่รับประกันว่าหลังจากการเรียกเธรดปัจจุบันให้ผลผลิต () เธรดอื่น ๆ ที่มีลำดับความสำคัญเดียวกันจะได้รับสิทธิ์ในการดำเนินการอย่างแน่นอน นอกจากนี้ยังเป็นไปได้ว่าเธรดปัจจุบันจะเข้าสู่ "สถานะการรัน" และทำงานต่อไป
ให้ผลผลิตและรอ
(01) รอ () คือการให้เธรดป้อน "รอ (บล็อก) สถานะ" จาก "สถานะการทำงาน" ในขณะที่ไม่ให้ผลผลิต () คือการให้เธรดป้อน "สถานะพร้อม" จาก "สถานะการทำงาน"
(02) WAIT () คือการล็อคการซิงโครไนซ์ที่จะปล่อยเธรดปล่อยวัตถุที่เก็บไว้ในขณะที่วิธีการให้ () จะไม่ปล่อยล็อค
(03) การรอเป็นวิธีการของวัตถุผลผลิตคือวิธีการเธรด
ด้ายสลีป
ฟังก์ชั่นของ Sleep () คือการปล่อยให้ด้ายปัจจุบันนอนหลับนั่นคือเธรดปัจจุบันจะเข้าสู่ "สถานะการทำงาน" ไปยังสถานะ "การนอนหลับ (บล็อก)" sleep () จะระบุเวลานอนหลับและเวลานอนด้ายจะมากกว่า/เท่ากับเวลานอน เมื่อเธรดถูกปลุกอีกครั้งมันจะเปลี่ยนจาก "สถานะการปิดกั้น" เป็น "สถานะพร้อม" รอให้ CPU ถูกกำหนดให้ดำเนินการ
ความแตกต่างระหว่างการนอนหลับกับการรอ
ฟังก์ชั่นของ WAIT () คือการอนุญาตให้เธรดปัจจุบันเข้าสู่สถานะ "รอ (บล็อก) จาก" สถานะการรัน "และปล่อยล็อคการซิงโครไนซ์ฟังก์ชั่นของ SLEEP () คือการปล่อยให้เธรดปัจจุบันเข้าสู่" สถานะการนอนหลับ (บล็อก) จาก "สถานะการทำงาน" (จริง ๆ แล้วมันไม่แตกต่างกันมาก)
รอ () ปล่อยล็อคการซิงโครไนซ์ของวัตถุในขณะที่ sleep () ไม่ปล่อยล็อค
รอคือวิธีการของวัตถุการนอนหลับเป็นวิธีการเธรด
เข้าร่วม
ให้เธรดหลักรอและเธรดลูกสามารถทำงานต่อไปหลังจากที่เธรดหลักเสร็จสมบูรณ์
ขัดจังหวะ
ใช้เพื่อยุติเธรดที่ถูกบล็อก
@overridepublic เป็นโมฆะ Run () {ลอง {ในขณะที่ (จริง) {// ดำเนินการงาน ... }} catch (interruptedException IE) {// เนื่องจากข้อยกเว้น interruptedException ออกจากขณะที่ (จริง) ลูปและเธรดสิ้นสุด! -ในขณะที่ (จริง) การเรียกร้องให้ขัดจังหวะ () ของเธรดสร้างการขัดจังหวะการขัดจังหวะการขัดจังหวะ การจับที่ถูกขัดจังหวะอยู่ข้างนอกในขณะที่ (จริง) ดังนั้นจึงออกจากการวนซ้ำ (จริง)
ยุติเธรดในสถานะการรัน
@overridepublic เป็นโมฆะ Run () {ในขณะที่ (! isInterrupted ()) {// งานเรียกใช้งาน ... }}วิธีทั่วไปในการยุติเธรด
@OverridePublic เป็นโมฆะ Run () {ลอง {// 1. isInterrupted () รับประกันว่าเธรดจะถูกยกเลิกตราบใดที่การขัดจังหวะถูกทำเครื่องหมายจริง ในขณะที่ (! isInterrupted ()) {// ดำเนินการงาน ... }} catch (interruptedException IE) {// 2. InterruptedException Exception รับประกันว่าเมื่อมีข้อยกเว้น interruptedException เกิดขึ้นเธรดจะถูกยกเลิก -
ลำดับความสำคัญของเธรด
ช่วงลำดับความสำคัญของเธรดใน Java คือ 1 ถึง 10 และลำดับความสำคัญเริ่มต้นคือ 5 "เธรดลำดับความสำคัญสูง" จะนำหน้าการดำเนินการผ่าน "เธรดลำดับความสำคัญต่ำ" มีสองประเภทของเธรดใน Java: เธรดผู้ใช้และเธรด daemon พวกเขาสามารถแยกแยะได้ด้วยวิธี Isdaemon (): หากถูกส่งคืนเท็จหมายความว่าเธรดเป็น "เธรดผู้ใช้"; มิฉะนั้นจะเป็น "ด้ายดุล" โดยทั่วไปเธรดผู้ใช้จะทำงานระดับผู้ใช้ในขณะที่เธรด daemon ยังเป็น "เธรดแบ็กเอนด์" ซึ่งโดยทั่วไปจะใช้ในการทำงานพื้นหลัง ควรสังเกตว่าเครื่องเสมือน Java จะออกหลังจาก "เธรดผู้ใช้" เสร็จสมบูรณ์
แต่ละเธรดมีลำดับความสำคัญ "เธรดลำดับความสำคัญสูง" จะนำหน้าการดำเนินการผ่าน "เธรดลำดับความสำคัญต่ำ" แต่ละเธรดสามารถทำเครื่องหมายเป็น daemon หรือ non-daemon เมื่อสร้างเธรดเด็กใหม่ในเธรดหลักที่กำลังทำงานอยู่ลำดับความสำคัญของเธรดลูกจะถูกตั้งค่าเป็น "ลำดับความสำคัญของเธรดหลักที่สร้างขึ้น" และ "เธรดลูกจะเป็นเธรด daemon" เมื่อใดและเฉพาะถ้า "เธรดหลักที่สร้างมันเป็นเธรด daemon"
เมื่อเครื่องเสมือนของ Java เริ่มต้นขึ้นมักจะมีเธรดที่ไม่ใช่ Daemon เดียว (เธรดนี้เริ่มต้นด้วยวิธีการหลัก ()) JVM จะทำงานจนกว่าจะมีเงื่อนไขใด ๆ ต่อไปนี้เกิดขึ้นและ JVM จะยุติการวิ่ง:
(01) วิธีการทางออก () เรียกว่าและออก () ได้รับอนุญาตให้ดำเนินการตามปกติ
(02) "ด้ายที่ไม่ใช่ดอน" ทั้งหมดนั้นตายไปแล้ว (นั่นคือมีเพียง "เธรด daemon" ใน JVM)
เสื้อกล้าม
(01) เธรดหลักหลักคือเธรดผู้ใช้และเธรดลูก T1 ที่สร้างขึ้นก็เป็นเธรดผู้ใช้
(02) T2 เป็นด้ายดุล เมื่อ "Main Thread Main" และ "Sub-Thread T1" (เป็นทั้งเธรดผู้ใช้) จะถูกดำเนินการและมีเพียงเธรด Daemon T2 เท่านั้นที่เหลือ JVM จะออกโดยอัตโนมัติ
ปัญหาผู้ผลิตและผู้บริโภค
(01) ผู้ผลิตจะผลิตเฉพาะเมื่อคลังสินค้าไม่เต็มและหยุดการผลิตเมื่อคลังสินค้าเต็ม
(02) ผู้บริโภคสามารถบริโภคได้ก็ต่อเมื่อพวกเขามีผลิตภัณฑ์ในการจัดเก็บและรอว่าพวกเขามีคลังสินค้าว่างเปล่า
(03) เมื่อผู้บริโภคพบว่าไม่มีผลิตภัณฑ์ที่จะบริโภคในคลังสินค้าพวกเขาจะแจ้งผู้ผลิต
(04) เมื่อผู้ผลิตผลิตผลิตภัณฑ์อุปถัมภ์พวกเขาควรแจ้งให้ผู้บริโภคที่รอคอยบริโภค
การสื่อสารระหว่างเธรด
วิธีการ: หน่วยความจำที่ใช้ร่วมกันและการส่งข้อความ
หน่วยความจำที่ใช้ร่วมกัน: เธรด A และเธรด B แชร์หน่วยความจำเธรด A อัปเดตค่าของตัวแปรที่ใช้ร่วมกันรีเฟรชเป็นหน่วยความจำหลักและเธรด B ไปที่หน่วยความจำหลักเพื่ออ่านตัวแปรที่อัปเดตของเธรด A กระบวนการสื่อสารทั้งหมดจะต้องผ่านหน่วยความจำหลัก การซิงโครไนซ์ดำเนินการอย่างชัดเจน
หากตัวแปรเป็นประเภทผันผวนการอ่านและการเขียนของตัวแปรจะเป็นอะตอม หากเป็นการดำเนินการที่ระเหยได้หลายครั้งหรือการดำเนินการคอมโพสิตคล้ายกับผันผวน ++ การดำเนินการเหล่านี้จะไม่เป็นอะตอมอย่างครบถ้วน
ตัวแปรผันผวนนั้นมีลักษณะดังต่อไปนี้:
[การมองเห็น]: เมื่ออ่านตัวแปรผันผวนคุณสามารถเห็นการเขียนล่าสุดไปยังตัวแปรผันผวน (เธรดใด ๆ )
[Atomicity]: มันมีความเป็นอะตอมสำหรับการอ่าน/เขียนของตัวแปรระเหยง่ายใด ๆ แต่มันไม่มีอะตอมมิกสำหรับการดำเนินการแบบผสมคล้ายกับผันผวน ++
การเขียนผันผวน: เมื่อเขียนตัวแปรผันผวน JMM จะล้างตัวแปรที่ใช้ร่วมกันในหน่วยความจำท้องถิ่นที่สอดคล้องกับเธรดไปยังหน่วยความจำหลัก
การอ่านผันผวน: เมื่ออ่านตัวแปรผันผวน JMM จะทำให้หน่วยความจำในท้องถิ่นนั้นสอดคล้องกับเธรด เธรดจะอ่านตัวแปรที่ใช้ร่วมกันจากหน่วยความจำหลัก
การส่งข้อความ: การส่งข้อความจะดำเนินการโดยปริยายก่อนที่จะยอมรับข้อความ
ด้าย
ThreadLocal ไม่ได้ใช้เพื่อแก้ปัญหาการเข้าถึงแบบหลายเธรดไปยังวัตถุที่ใช้ร่วมกัน โดยทั่วไปการพูดวัตถุไปยังเธรดผ่าน threadlocal.set () เป็นวัตถุที่ใช้โดยเธรดเอง ไม่จำเป็นต้องเข้าถึงเธรดอื่น ๆ และไม่สามารถเข้าถึงได้ Threadlocal อนุญาตให้แต่ละเธรดสามารถรักษาวัตถุอิสระของตัวเองได้ มันไม่ได้ถูกนำไปใช้ผ่าน threadlocal.set () แต่วัตถุที่สร้างขึ้นโดยการทำงานของวัตถุใหม่ในแต่ละเธรด แต่ละเธรดสร้างหนึ่งไม่ใช่สำเนาหรือสำเนาของวัตถุ การอ้างอิงถึงวัตถุที่สร้างขึ้นใหม่จะถูกบันทึกลงในแผนที่แต่ละเธรดของแต่ละเธรดผ่าน threadlocal.set () แต่ละเธรดมีแผนที่เช่นนี้ เมื่อมีการดำเนินการ threadlocal.get () แต่ละเธรดจะนำวัตถุออกมาจากแผนที่ของตัวเอง ดังนั้นสิ่งที่นำออกมาคือวัตถุในแต่ละเธรด อินสแตนซ์ ThreadLocal ใช้เป็นคีย์แผนที่ หากสิ่งที่ threadlocal.set () เข้าสู่วัตถุเดียวกันที่ใช้ร่วมกันโดยหลายเธรดดังนั้น threadlocal.get () ของหลายเธรดยังคงได้รับวัตถุที่ใช้ร่วมกันและยังมีปัญหาการเข้าถึงพร้อมกัน
การใช้งาน ThreadLocal
นำเข้า Java.util.Collections; นำเข้า java.util.hashmap; นำเข้า java.util.map; /** * คลาสที่ใช้ ThreadLocal * * @author Leizhimin 2010-1-5 10:35:27 */คลาสสาธารณะ MythreadLocal {// กำหนดตัวแปร ThreadLocal เพื่อบันทึกข้อมูล int หรือ Integer ส่วนตัว com.lavasoft.test2.threadlocal ได้รับการป้องกันจำนวนเต็ม initialValue () {return 0; - จำนวนเต็มสาธารณะ getNextNum () {// รับค่าของ TL และเพิ่ม 1 และอัปเดตค่าของ t1 tl.Set (tl.get () + 1); กลับ tl.get (); }} คลาส ThreadLocal <t> {แผนที่ส่วนตัว <เธรด, t> map = collections.synchronizedmap (ใหม่ hashmap <เธรด, t> ()); Public ThreadLocal () {} ได้รับการป้องกัน t initialValue () {return null; } สาธารณะ t get () {เธรด t = thread.currentthread (); t obj = map.get (t); if (obj == null &&! map.containskey (t)) {obj = initialValue (); map.put (t, obj); } return obj; } ชุดโมฆะสาธารณะ (ค่า t) {map.put (thread.currentthread (), ค่า); } โมฆะสาธารณะลบ () {map.remove (thread.currentthread ()); -ในความเป็นจริง Threadlocal ทำสิ่งนี้:
สาธารณะ t get () {เธรด t = thread.currentthread (); threadlocalMap map = getMap (t); if (map! = null) {threadlocalmap.entry e = map.getEntry (นี่); ถ้า (e! = null) return (t) e.value; } return setInitialValue (); -