1. สร้างกระทู้
มีสองวิธีในการสร้างเธรดใน Java: การใช้คลาส Thread และการใช้อินเทอร์เฟซ Runnable เมื่อใช้อินเทอร์เฟซ Runnable คุณจะต้องสร้างอินสแตนซ์ Thread ดังนั้น ไม่ว่าคุณจะสร้างเธรดผ่านคลาส Thread หรืออินเทอร์เฟซ Runnable คุณต้องสร้างอินสแตนซ์ของคลาส Thread หรือคลาสย่อย ตัวสร้างเธรด:
วิธีที่ 1: สืบทอดคลาสเธรดและแทนที่วิธีการเรียกใช้
-
-
การสาธิตคลาสขยายเธรด {
โมฆะสาธารณะวิ่ง () {
สำหรับ(int i=0;i<60;i++){
System.out.println(Thread.currentThread().getName()+i);
-
-
-
เช่นเดียวกับผู้คนที่เกิด แก่ ป่วย และตาย เส้นด้ายยังต้องผ่านสถานะที่แตกต่างกันสี่สถานะ: เริ่มต้น (รอ) วิ่ง หยุด และหยุด สถานะทั้งสี่นี้สามารถควบคุมได้โดยใช้วิธีการในคลาสเธรด วิธีการที่เกี่ยวข้องกับสถานะทั้งสี่นี้ในคลาสเธรดมีดังต่อไปนี้
เธรดไม่ได้รันโค้ดในวิธีการ run ทันทีหลังจากถูกสร้างขึ้น แต่อยู่ในสถานะรอ เมื่อเธรดอยู่ในสถานะรอ คุณสามารถตั้งค่าคุณลักษณะต่างๆ ของเธรดผ่านวิธีการของคลาสเธรด เช่น ลำดับความสำคัญของเธรด (setPriority) ชื่อเธรด (setName) และประเภทเธรด (setDaemon)
เมื่อมีการเรียกเมธอด start เธรดจะเริ่มรันโค้ดในเมธอด run เธรดเข้าสู่สถานะกำลังทำงาน คุณสามารถใช้เมธอดisAliveของคลาสเธรดเพื่อตรวจสอบว่าเธรดกำลังทำงานอยู่หรือไม่ เมื่อเธรดอยู่ในสถานะกำลังทำงาน isAlive จะส่งกลับค่าจริง เมื่อ isAlive ส่งคืนค่าเท็จ เธรดอาจอยู่ในสถานะรอหรืออยู่ในสถานะหยุดทำงาน รหัสต่อไปนี้สาธิตการสลับระหว่างสามสถานะของการสร้างเธรด การรันและการหยุด และส่งออกค่าที่ส่งคืน isAlive ที่สอดคล้องกัน
เมื่อเธรดเริ่มดำเนินการตามวิธีการเรียกใช้ เธรดนั้นจะไม่ออกจนกว่าวิธีการเรียกใช้จะเสร็จสมบูรณ์ อย่างไรก็ตาม ในระหว่างการดำเนินการของเธรด มีสองวิธีที่สามารถใช้เพื่อหยุดการดำเนินการของเธรดชั่วคราวได้ ทั้งสองวิธีนี้ถูกระงับและนอนหลับ หลังจากใช้การระงับเพื่อระงับเธรด คุณสามารถปลุกเธรดนั้นได้ด้วยวิธีการดำเนินการต่อ หลังจากใช้โหมดสลีปเพื่อทำให้เธรดสลีป เธรดสามารถอยู่ในสถานะพร้อมหลังจากเวลาที่ตั้งไว้เท่านั้น (หลังจากเธรดสลีปสิ้นสุดลง เธรดอาจไม่ดำเนินการทันที แต่จะเข้าสู่สถานะพร้อมเท่านั้น โดยรอให้ระบบกำหนดเวลา) .
มีสองประเด็นที่ควรทราบเมื่อใช้วิธีการสลีป:
1. วิธีสลีปมีสองรูปแบบที่โอเวอร์โหลด หนึ่งในรูปแบบที่โอเวอร์โหลดไม่เพียงแต่สามารถตั้งค่ามิลลิวินาทีเท่านั้น แต่ยังรวมถึงนาโนวินาทีด้วย (1,000,000 นาโนวินาทีเท่ากับ 1 มิลลิวินาที) อย่างไรก็ตาม เครื่องเสมือน Java บนแพลตฟอร์มระบบปฏิบัติการส่วนใหญ่ไม่แม่นยำถึงระดับนาโนวินาที ดังนั้น หากตั้งค่าระดับนาโนวินาทีสำหรับโหมดสลีป เครื่องเสมือน Java จะใช้เวลามิลลิวินาทีที่ใกล้เคียงกับค่านี้มากที่สุด
2. ต้องใช้ Throws หรือ try{...}catch{...} เมื่อใช้วิธีการ sleep เนื่องจากเมธอด run ไม่สามารถใช้การพ่นได้ คุณจึงสามารถใช้ได้เพียง try{...}catch{...} เท่านั้น เมื่อเธรดอยู่ในโหมดสลีปและใช้วิธีการขัดจังหวะเพื่อขัดจังหวะเธรด โหมดสลีปจะส่ง InterruptedException วิธีการนอนหลับถูกกำหนดดังนี้:
มีสามวิธีในการยุติเธรด
1. ใช้แฟล็ก exit เพื่อทำให้เธรดออกจากการทำงานตามปกติ นั่นคือ เธรดยุติลงเมื่อวิธีการรันเสร็จสมบูรณ์
2. ใช้วิธีหยุดเพื่อบังคับยุติเธรด (ไม่แนะนำวิธีนี้ เนื่องจากการหยุด เช่น การหยุดชั่วคราวและดำเนินการต่อ อาจให้ผลลัพธ์ที่คาดเดาไม่ได้เช่นกัน)
3. ใช้วิธีขัดจังหวะเพื่อขัดจังหวะเธรด
1. ยุติเธรดโดยใช้แฟล็กทางออก
เมื่อดำเนินการวิธีการรัน เธรดจะออก แต่บางครั้งวิธีการวิ่งก็ไม่มีวันสิ้นสุด ตัวอย่างเช่น มีการใช้เธรดในโปรแกรมเซิร์ฟเวอร์เพื่อตรวจสอบคำขอของไคลเอ็นต์ หรืองานอื่นๆ ที่ต้องมีการประมวลผลแบบวนรอบ ในกรณีนี้ งานเหล่านี้มักจะอยู่ในลูป เช่น ลูป while หากคุณต้องการให้ลูปทำงานตลอดไป คุณสามารถใช้ while(true){...} เพื่อจัดการมันได้ แต่ถ้าคุณต้องการออกจากลูป while ภายใต้เงื่อนไขบางประการ วิธีที่ตรงที่สุดก็คือการตั้งค่าแฟล็กประเภทบูลีน และตั้งค่าแฟล็กนี้เป็นจริงหรือเท็จเพื่อควบคุมว่าจะออกจากลูป while หรือไม่ ตัวอย่างของการยกเลิกเธรดโดยใช้แฟล็ก exit มีดังต่อไปนี้
ฟังก์ชันของวิธีการรวมคือการทำให้เธรดการดำเนินการแบบอะซิงโครนัสกลายเป็นการดำเนินการแบบซิงโครนัส กล่าวคือ เมื่อมีการเรียกเมธอด start ของอินสแตนซ์เธรด เมธอดนี้จะกลับมาทันที หากคุณต้องการใช้ค่าที่คำนวณโดยเธรดนี้หลังจากเรียกเมธอด start คุณต้องใช้วิธีการรวม หากคุณไม่ได้ใช้วิธีรวม ไม่มีการรับประกันว่าเมื่อมีการดำเนินการคำสั่งที่ตามหลังวิธี start เธรดจะถูกดำเนินการ หลังจากใช้วิธีการรวม โปรแกรมจะไม่ดำเนินการต่อไปจนกว่าเธรดนี้จะออกจากการทำงาน รหัสต่อไปนี้แสดงให้เห็นถึงการใช้การเข้าร่วม
3. ปัญหาด้านความปลอดภัยแบบมัลติเธรด
สาเหตุของปัญหา: เมื่อหลายคำสั่งดำเนินการบนเธรดเดียวกันและใช้ข้อมูลร่วมกัน เธรดเดียวจะดำเนินการเพียงส่วนหนึ่งของคำสั่งหลาย ๆ ก่อนที่การดำเนินการจะเสร็จสิ้น เธรดอื่นจะมีส่วนร่วมในการดำเนินการ ส่งผลให้เกิดข้อผิดพลาดของข้อมูลที่ใช้ร่วมกัน
วิธีแก้ไข: สำหรับหลายคำสั่งที่ทำงานบนข้อมูลที่ใช้ร่วมกัน เธรดเดียวเท่านั้นที่สามารถดำเนินการได้ ในระหว่างกระบวนการดำเนินการ เธรดอื่นจะไม่ดำเนินการ
บล็อกโค้ดแบบซิงโครนัส:
-
-
-
-
โมฆะสาธารณะวิ่ง () {
ในขณะที่ (จริง) {
ขายตั๋ว();
-
-
-
การสื่อสารระหว่างเธรด
x=(x+1)%2;
-
-
}).เริ่ม();
เธรดใหม่ (Runnable ใหม่ () {
โมฆะสาธารณะวิ่ง () {
ในขณะที่ (จริง) {
p.get();
-
-
}).เริ่ม();
-
-
-
จางซาน....ชาย จางซาน....ชาย
ลิลี่....นว
ลิลี่....ชาย จางซาน....nv
ลิลี่....ชาย
-
-
-
}).เริ่ม();
เธรดใหม่ (Runnable ใหม่ () {
โมฆะสาธารณะวิ่ง () {
ในขณะที่ (จริง) {
ซิงโครไนซ์ (p) {
p.get();
-
-
-
}).เริ่ม();
-
-
-
ลิลี่....นว
ลิลี่....นว
ลิลี่....นว
ลิลี่....นว
ลิลี่....นว
ลิลี่....นว
จางซาน....ชาย จางซาน....ชาย จางซาน....ชาย จางซาน....ชาย
-
-
-
}).เริ่ม();
เธรดใหม่ (Runnable ใหม่ () {
โมฆะสาธารณะวิ่ง () {
ในขณะที่ (จริง) {
ซิงโครไนซ์ (p) {
ถ้า(!แฟล็ก)
พยายาม {
p.รอ();
} จับ (InterruptedException e) {
// TODO บล็อก catch ที่สร้างขึ้นอัตโนมัติ
e.printStackTrace();
-
p.get();
แฟล็ก =เท็จ;
p.notifyAll();
-
-
-
}).เริ่ม();
-
-
-
สินค้าขั้นสุดท้าย g = สินค้าใหม่ ();
เธรดใหม่ (Runnable ใหม่ () {
โมฆะสาธารณะวิ่ง () {
ในขณะที่ (จริง) {
g.ผลิต("สินค้า");
-
-
}).เริ่ม();
เธรดใหม่ (Runnable ใหม่ () {
โมฆะสาธารณะวิ่ง () {
ในขณะที่ (จริง) {
g.บริโภค();
-
-
}).เริ่ม();
-
-
-
สินค้าขั้นสุดท้าย g = สินค้าใหม่ ();
เธรดใหม่ (Runnable ใหม่ () {
โมฆะสาธารณะวิ่ง () {
ในขณะที่ (จริง) {
g.ผลิต("สินค้า");
-
-
},"ผู้ผลิตหมายเลข 1").start();
เธรดใหม่ (Runnable ใหม่ () {
โมฆะสาธารณะวิ่ง () {
ในขณะที่ (จริง) {
g.ผลิต("สินค้า");
-
-
},"ผู้ผลิตหมายเลข 2").start();
เธรดใหม่ (Runnable ใหม่ () {
โมฆะสาธารณะวิ่ง () {
ในขณะที่ (จริง) {
g.บริโภค();
-
-
},"ผู้บริโภคหมายเลข 1").start();
เธรดใหม่ (Runnable ใหม่ () {
โมฆะสาธารณะวิ่ง () {
ในขณะที่ (จริง) {
g.บริโภค();
-
-
},"ผู้บริโภคหมายเลข 2").start();
-
-
-
ผู้บริโภคหมายเลข 2 บริโภคหมายเลขผลิตภัณฑ์ ******: 48049
โปรดิวเซอร์หนึ่งผลิต....หมายเลขสินค้า: 48050
ผู้บริโภคหมายเลข 1 บริโภคหมายเลขผลิตภัณฑ์ ******: 48050
โปรดิวเซอร์หนึ่งผลิต....หมายเลขสินค้า: 48051
ผู้บริโภคหมายเลข 2 บริโภคหมายเลขผลิตภัณฑ์ ******: 48051
ผู้ผลิตหมายเลข 2 ผลิต....หมายเลขสินค้า: 48052
ผู้บริโภคหมายเลข 2 บริโภคหมายเลขผลิตภัณฑ์ ******: 48052
โปรดิวเซอร์หนึ่งผลิต....หมายเลขสินค้า: 48053
ผู้บริโภคหมายเลข 1 บริโภคหมายเลขผลิตภัณฑ์ ******: 48053
โปรดิวเซอร์หนึ่งผลิต....หมายเลขสินค้า: 48054
ผู้บริโภคหมายเลข 2 บริโภคหมายเลขผลิตภัณฑ์ ******: 48054
ผู้ผลิตหมายเลข 2 ผลิต....หมายเลขสินค้า: 48055
ผู้บริโภคหมายเลข 2 บริโภคหมายเลขผลิตภัณฑ์ ******: 48055
-