Java multithreading (หนึ่ง)
เป็นจุดความรู้ที่สำคัญมากในชวามันยังจำเป็นที่จะต้องสรุปที่นี่
1. วงจรชีวิตของเธรดและสถานะพื้นฐานทั้งห้า
เกี่ยวกับวงจรชีวิตของเธรดใน Java มาดูภาพคลาสสิกต่อไปนี้ก่อน:
ภาพข้างต้นโดยทั่วไปครอบคลุมถึงจุดความรู้ที่สำคัญของมัลติเธรดในชวา เมื่อคุณเชี่ยวชาญคะแนนความรู้ในรูปด้านบนโดยทั่วไปคุณจะได้ควบคุมการทำมัลติเธรดในชวา ส่วนใหญ่รวมถึง:
กระทู้ Java มีห้าสถานะพื้นฐาน
สถานะใหม่ (ใหม่): เมื่อมีการสร้างคู่วัตถุเธรดมันจะเข้าสู่สถานะใหม่เช่น: เธรด t = new mythread ();
ready state (runnable): เมื่อวิธีการเริ่มต้น () ของวัตถุเธรด (t.start ();) เธรดจะเข้าสู่สถานะพร้อม เธรดในสถานะพร้อมหมายความว่าเธรดพร้อมและกำลังรอให้ CPU กำหนดเวลาดำเนินการได้ตลอดเวลาไม่ใช่ว่าเธรดจะดำเนินการทันทีหลังจาก T.Start () ถูกดำเนินการ;
สถานะการรัน: เมื่อซีพียูเริ่มกำหนดเวลาเธรดในสถานะพร้อมเธรดสามารถดำเนินการได้อย่างแท้จริงนั่นคือมันเข้าสู่สถานะการทำงาน หมายเหตุ: สถานะพร้อมเป็นเพียงรายการเดียวที่อยู่ในสถานะการทำงานนั่นคือถ้าเธรดต้องการเข้าสู่สถานะการรันเพื่อดำเนินการจะต้องอยู่ในสถานะพร้อมก่อน
สถานะที่ถูกบล็อก: ด้วยเหตุผลบางอย่างเธรดในสถานะการทำงานชั่วคราวจะทำให้การใช้งาน CPU ชั่วคราวและหยุดการดำเนินการ ในเวลานี้มันเข้าสู่สถานะการบล็อก CPU จะไม่มีโอกาสถูกเรียกโดย CPU อีกครั้งเพื่อเข้าสู่สถานะการทำงาน ตามเหตุผลของการบล็อกสถานะการบล็อกสามารถแบ่งออกเป็นสามประเภท:
1. การรอการบล็อก: เธรดในสถานะการทำงานจะดำเนินการวิธีการรอ () เพื่อให้เธรดป้อนสถานะการรอการปิดกั้น;
2. การปิดกั้นแบบซิงโครไนซ์- เธรดไม่สามารถรับล็อคซิงโครไนซ์ซิงโครไนซ์ได้ (เนื่องจากล็อคถูกครอบครองโดยเธรดอื่น ๆ ) มันจะเข้าสู่สถานะการปิดกั้นแบบซิงโครไนซ์
3. การปิดกั้นอื่น ๆ-เธรดจะเข้าสู่สถานะการบล็อกโดยเรียก Sleep () หรือเข้าร่วม () ของเธรดหรือออกคำขอ I/O เมื่อสถานะการนอนหลับ () หมดเวลาเข้าร่วม () รอให้เธรดยุติหรือหมดเวลาหรือการประมวลผล I/O เสร็จสิ้นเธรดจะกลับเข้าสู่สถานะพร้อมอีกครั้ง
Dead: เธรดเสร็จสิ้นการดำเนินการหรือออกจากวิธีการเรียกใช้ () เนื่องจากข้อยกเว้นและเธรดจะสิ้นสุดวงจรชีวิต
2. การสร้างและการเริ่มต้นของ Java Multithreads
มีสามรูปแบบพื้นฐานของการสร้างเธรดใน Java
1. สืบทอดคลาสเธรดและแทนที่วิธีการเรียกใช้ () ของคลาส
คลาส MyThread ขยายเธรด {ส่วนตัว int i = 0; @Override โมฆะสาธารณะเรียกใช้ () {สำหรับ (i = 0; i <100; i ++) {system.out.println (thread.currentthread (). getName () + "" + i); - Public Class Threadtest {โมฆะสาธารณะคงที่หลัก (สตริง [] args) {สำหรับ (int i = 0; i <100; i ++) {system.out.println (thread.currentthread (). getName () + "" + i); if (i == 30) {เธรด MyThread1 = new MyThread (); // สร้างเธรดใหม่ MyThread1 เธรดนี้เข้าสู่เธรดสถานะใหม่ MyThread2 = ใหม่ MyThread (); // สร้างเธรดใหม่ MyThread2 เธรดนี้เข้าสู่สถานะใหม่ MYTHREAD1.START (); // เรียกเมธอด start () เพื่อให้เธรดป้อนสถานะ ready state mythread2.start (); // เรียกเมธอด start () เพื่อให้เธรดป้อนสถานะ ready}}}}}ดังที่แสดงไว้ข้างต้นการสืบทอดคลาสเธรดคลาสเธรดใหม่ MyThread ถูกกำหนดโดยการเขียนทับวิธีการเรียกใช้ () ซึ่งวิธีการของวิธีการของวิธีการเรียกใช้ () แสดงถึงงานที่เธรดจำเป็นต้องทำให้เสร็จสมบูรณ์และเรียกว่าร่างกายการดำเนินการเธรด เมื่อสร้างวัตถุเธรดคลาสนี้เธรดใหม่จะถูกสร้างขึ้นและเข้าสู่สถานะเธรดใหม่ โดยการเรียกวิธีการเริ่มต้น () ที่อ้างอิงโดยวัตถุเธรดเธรดจะเข้าสู่สถานะพร้อม ในเวลานี้เธรดอาจไม่ถูกดำเนินการทันทีขึ้นอยู่กับเวลาการกำหนดเวลา CPU
2. ใช้อินเทอร์เฟซ Runnable และแทนที่วิธี RUN () ของอินเตอร์เฟส วิธีการเรียกใช้ () ยังเป็นตัวดำเนินการเธรดสร้างอินสแตนซ์ของคลาสการใช้งานที่เรียกใช้งานได้และใช้อินสแตนซ์นี้เป็นเป้าหมายของคลาสเธรดเพื่อสร้างวัตถุเธรด วัตถุเธรดเป็นวัตถุเธรดจริง
คลาส myrunnable onplunable runnable {private int i = 0; @Override โมฆะสาธารณะเรียกใช้ () {สำหรับ (i = 0; i <100; i ++) {system.out.println (thread.currentthread (). getName () + "" + i); - Public Class Threadtest {โมฆะสาธารณะคงที่หลัก (สตริง [] args) {สำหรับ (int i = 0; i <100; i ++) {system.out.println (thread.currentthread (). getName () + "" + i); if (i == 30) {runnable myrunnable = new myrunnable (); // สร้างวัตถุของเธรดการใช้งานการใช้งานที่เรียกใช้ได้เธรด 1 = เธรดใหม่ (myrunnable); // สร้างเธรดใหม่ด้วย myrunnable เป็นเธรด thread thread thread2 = เธรดใหม่ (myrunnable); Thread1.start (); // เรียกเมธอด start () เพื่อให้เธรดป้อน ready state thread2.start (); - ฉันเชื่อว่าทุกคนคุ้นเคยกับสองวิธีข้างต้นเพื่อสร้างเธรดใหม่ แล้วความสัมพันธ์ระหว่างเธรดและ Runnable คืออะไร? ก่อนอื่นให้ดูตัวอย่างต่อไปนี้
Public Class Threadtest {โมฆะสาธารณะคงที่หลัก (สตริง [] args) {สำหรับ (int i = 0; i <100; i ++) {system.out.println (thread.currentthread (). getName () + "" + i); if (i == 30) {runnable myrunnable = new myrunnable (); เธรดเธรด = ใหม่ MyThread (myrunnable); thread.start (); }}}} คลาส myrunnable onplunable runnable {private int i = 0; @Override โมฆะสาธารณะ Run () {system.out.println ("ในการวิ่ง myrunnable"); สำหรับ (i = 0; i <100; i ++) {system.out.println (thread.currentthread (). getName () + "" + i); }}} คลาส MyThread ขยายเธรด {private int i = 0; Public Mythread (Runnable Runnable) {super (runnable); } @Override โมฆะสาธารณะ Run () {system.out.println ("ใน MyThread Run"); สำหรับ (i = 0; i <100; i ++) {system.out.println (thread.currentthread (). getName () + "" + i); -ในทำนองเดียวกันก็เป็นจริงสำหรับการสร้างเธรดที่ใช้อินเตอร์เฟสที่เรียกใช้งานได้ความแตกต่างคือ
1 เธรด = ใหม่ MyThread (myrunnable);
ดังนั้นวิธีนี้สามารถสร้างเธรดใหม่ได้สำเร็จหรือไม่? คำตอบคือใช่ สำหรับการทำงานของเธรดในเวลานี้วิธีการเรียกใช้ () ในอินเตอร์เฟส myrunnable หรือวิธีการเรียกใช้ () ในคลาส Mythread หรือไม่? ผ่านเอาต์พุตเรารู้ว่าร่างกายการดำเนินการเธรดเป็นวิธีการเรียกใช้ () ในคลาส MyThread ในความเป็นจริงเหตุผลนั้นง่ายมากเนื่องจากคลาสเธรดเองยังใช้อินเทอร์เฟซที่รันได้และวิธีการเรียกใช้ () ถูกกำหนดไว้เป็นครั้งแรกในอินเตอร์เฟสที่เรียกใช้ได้
อินเทอร์เฟซสาธารณะ runnable {public abstract void run (); - ลองมาดูการใช้วิธีการเรียกใช้ () ในอินเทอร์เฟซ Runnable ในคลาสเธรด:
@Override โมฆะสาธารณะเรียกใช้ () {ถ้า (เป้าหมาย! = null) {target.run (); -กล่าวคือเมื่อดำเนินการวิธีการเรียกใช้ () ในคลาสเธรดมันจะพิจารณาก่อนว่าเป้าหมายมีอยู่ก่อน หากมีอยู่วิธีการเรียกใช้ () ในเป้าหมายจะถูกดำเนินการนั่นคือวิธีการเรียกใช้ () ในคลาสที่ใช้อินเตอร์เฟส runnable และเขียนทับวิธีการเรียกใช้ () อย่างไรก็ตามในคอลัมน์ที่ระบุไว้ข้างต้นเนื่องจากการมีอยู่ของ polymorphism วิธีการเรียกใช้ () ในคลาสเธรดไม่ได้ดำเนินการเลย แต่ประเภทรันไทม์นั่นคือวิธีการเรียกใช้ () ในคลาส Mythread จะถูกดำเนินการโดยตรง
3. สร้างเธรดโดยใช้อินเทอร์เฟซ callable และในอนาคต โดยเฉพาะมันสร้างคลาสการใช้งานสำหรับอินเตอร์เฟส callable และใช้วิธี clall () และใช้คลาส FutureTask เพื่อห่อวัตถุคลาสการใช้งานที่เรียกได้และใช้วัตถุ FutureTask นี้เป็นเป้าหมายของวัตถุเธรดเพื่อสร้างเธรด
ดูเหมือนจะซับซ้อนเล็กน้อย แต่จะชัดเจนถ้าคุณดูตัวอย่างโดยตรง
Public Class Threadtest {โมฆะสาธารณะคงที่หลัก (สตริง [] args) {callable <teger> myCallable = new myCallable (); // สร้าง MyCallable Object FutureTask <Integer> ft = New FutureTask <Integer> (MyCallable); // ใช้ futureTask เพื่อห่อวัตถุ mycallable สำหรับ (int i = 0; i <100; i ++) {system.out.println (thread.currentthread (). getName () + "" + i); if (i == 30) {เธรดเธรด = เธรดใหม่ (ft); // Object FutureTask สร้างเธรดใหม่เป็นเป้าหมายของเธรด thread.start (); // เธรดเข้าสู่สถานะ ready}} system.out.println ("เธรดหลักสำหรับลูปถูกดำเนินการแล้ว"); ลอง {int sum = ft.get (); // รับผลลัพธ์ที่ส่งคืนโดยเมธอดการโทร () ในระบบเธรดใหม่ที่สร้างขึ้นใหม่ out.println ("sum =" + sum); } catch (interruptedException e) {e.printStackTrace (); } catch (ExecutionException E) {E.printStackTrace (); }}} คลาส mycallable onplicement callable <teger> {private int i = 0; // ซึ่งแตกต่างจากวิธีการเรียกใช้ () วิธีการโทร () มีค่าส่งคืน @Override การเรียกใช้จำนวนเต็มสาธารณะ () {int sum = 0; สำหรับ (; i <100; i ++) {system.out.println (thread.currentthread (). getName () + "" + i); sum += i; } return sum; -ก่อนอื่นเราพบว่าในการใช้อินเทอร์เฟซ callable วิธีการเรียกใช้ () ไม่ได้เป็นวิธีการเรียกใช้ () อีกต่อไป แต่วิธีการโทร () วิธีการโทร () นี้เป็นตัวดำเนินการเธรดและยังมีค่าส่งคืน! เมื่อสร้างเธรดใหม่วัตถุ MyCallable จะถูกห่อหุ้มผ่าน FutureTask และยังทำหน้าที่เป็นเป้าหมายสำหรับวัตถุเธรด จากนั้นดูคำจำกัดความของคลาส FutureTask:
Public Class FutureTask <v> ใช้ RunnableFuture <v> {// .... } อินเทอร์เฟซสาธารณะ runnablefuture <v> ขยาย runnable, future <v> {void run (); -ดังนั้นเราจึงพบว่าคลาส FutureTask ใช้อินเทอร์เฟซทั้งที่วิ่งได้และในอนาคตซึ่งทำให้มีลักษณะคู่ของอนาคตและวิ่งได้ ผ่านคุณสมบัติ Runnable มันสามารถใช้เป็นเป้าหมายของวัตถุเธรดและคุณสมบัติในอนาคตช่วยให้ได้รับค่าส่งคืนของวิธีการโทร () ในเธรดที่สร้างขึ้นใหม่
หลังจากดำเนินการโปรแกรมนี้เราพบว่า SUM = 4950 เป็นผลลัพธ์สุดท้ายเสมอ "เธรดหลักสำหรับลูปถูกดำเนินการ ... " มีแนวโน้มที่จะส่งออกตรงกลางของลูปเธรดลูก จากกลไกการตั้งเวลาเธรดของ CPU เรารู้ว่าไม่มีปัญหากับช่วงเวลาเอาท์พุทของ "เธรดหลักสำหรับลูปถูกดำเนินการ ... " ดังนั้นทำไม Sum = 4950 จึงเป็นเอาต์พุตตลอดไป?
เหตุผลก็คือเมื่อวิธีการโทรของเธรด () ได้รับผ่านวิธี ft.get () เมื่อวิธีการของเธรดเด็กยังไม่ถูกดำเนินการวิธีการ ft.get () จะบล็อกจนกว่าวิธีการโทร () จะถูกดำเนินการก่อนที่จะได้รับค่าส่งคืน
ข้างต้นส่วนใหญ่อธิบายวิธีการสร้างเธรดทั่วไปสามวิธี สำหรับการเริ่มต้นของเธรดพวกเขาทั้งหมดเรียกว่าวิธีการเริ่มต้น () ของวัตถุเธรด เป็นสิ่งสำคัญที่จะต้องทราบว่าวิธีการเริ่มต้น () ไม่สามารถเรียกได้สองครั้งในวัตถุเธรดเดียวกัน
iii. พร้อมใช้งานการเรียกใช้และความตายของ Java multithreading
สถานะพร้อมจะถูกแปลงเป็นสถานะการทำงาน: เมื่อเธรดนี้ได้รับทรัพยากรโปรเซสเซอร์
สถานะการทำงานจะถูกแปลงเป็นสถานะพร้อม: เมื่อเธรดนี้เรียกวิธีการให้อัตราผลตอบแทน () หรือสูญเสียทรัพยากรโปรเซสเซอร์ในระหว่างการทำงาน
สถานะการทำงานจะถูกแปลงเป็นสถานะตาย: เมื่อร่างการดำเนินการเธรดเสร็จสมบูรณ์หรือมีข้อยกเว้นเกิดขึ้น
ควรสังเกตที่นี่ว่าเมื่อมีการเรียกวิธีการให้ผลผลิต () ของเธรดการเปลี่ยนเธรดจากสถานะการทำงานเป็นสถานะพร้อม แต่เธรดใดในสถานะพร้อมของ CPU ถูกกำหนดไว้มีการสุ่มบางอย่าง ดังนั้นจึงอาจเกิดขึ้นได้ว่าหลังจากเธรดเรียกวิธีการให้ผลผลิต () CPU ยังคงกำหนดเวลาเธรด A
เนื่องจากความต้องการทางธุรกิจที่เกิดขึ้นจริงมักพบว่าเธรดจะต้องถูกยกเลิกในโอกาสที่เฉพาะเจาะจงเพื่อให้เข้าสู่สถานะตาย วิธีการที่พบบ่อยที่สุดในปัจจุบันคือการตั้งค่าตัวแปรบูลีนและเมื่อตรงตามเงื่อนไขจะมีการดำเนินการตามเงื่อนไข ชอบ:
Public Class Threadtest {โมฆะสาธารณะคงที่หลัก (สตริง [] args) {myrunnable myrunnable = new myrunnable (); เธรด = เธรดใหม่ (myrunnable); สำหรับ (int i = 0; i <100; i ++) {system.out.println (thread.currentthread (). getName () + "" + i); if (i == 30) {thread.start (); } if (i == 40) {myrunnable.stopthread (); }}}} คลาส myrunnable ดำเนินการ runnable {private boolean stop; @Override โมฆะสาธารณะเรียกใช้ () {สำหรับ (int i = 0; i <100 &&! stop; i ++) {system.out.println (thread.currentthread (). getName () + "" + i); }} โมฆะสาธารณะ stopThread () {this.stop = true; -เราจะยังคงจัดเรียงบทความที่เกี่ยวข้องในอนาคต ขอบคุณสำหรับการสนับสนุนเว็บไซต์นี้!
ชุดของบทความ:
คำอธิบายของอินสแตนซ์หลายเธรด Java (i)
คำอธิบายโดยละเอียดเกี่ยวกับอินสแตนซ์แบบมัลติเธรด Java (II)
คำอธิบายโดยละเอียดเกี่ยวกับอินสแตนซ์แบบมัลติเธรด Java (iii)