สวัสดี! วันนี้เราจะพูดถึงมัลติเธรดต่อไป เรามาตรวจสอบคลาสเธรดและเมธอดสองสามอย่างที่ทำกัน เมื่อเราศึกษาวิธีการเรียนก่อนหน้านี้ เรามักจะเขียนสิ่งนี้: <ชื่อวิธีการ> -> <วิธีการทำอะไรบ้าง> สิ่งนี้ใช้ไม่ได้กับ
Thread
เมธอดของ ' :) พวกมันมีตรรกะที่ซับซ้อนกว่าซึ่งคุณไม่สามารถเข้าใจได้หากไม่มีตัวอย่าง
วิธีการ Thread.start()
เริ่มต้นด้วยการทำซ้ำตัวเอง อย่างที่คุณคงจำได้ คุณสามารถสร้างเธรดโดยทำให้คลาสของคุณสืบทอดคลาสThread
และแทนที่run()
เมธอด แต่มันจะไม่เริ่มเองแน่นอน ในการทำเช่นนี้ เราเรียกstart()
วิธีการ ของวัตถุของเรา จำตัวอย่างจากบทเรียนที่แล้ว:
public class MyFirstThread extends Thread {
@Override
public void run() {
System.out.println("Thread executed: " + getName());
}
}
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyFirstThread thread = new MyFirstThread();
thread.start();
}
}
}
หมายเหตุ:ในการเริ่มเธรด คุณต้องเรียกใช้start()
เมธอดพิเศษแทนrun()
เมธอด! นี่เป็นข้อผิดพลาดที่เกิดขึ้นได้ง่าย โดยเฉพาะอย่างยิ่งเมื่อคุณเริ่มเรียนมัลติเธรดเป็นครั้งแรก ในตัวอย่างของเรา หากคุณเรียกใช้run()
เมธอด 10 ครั้งแทนที่จะเป็นstart()
คุณจะได้สิ่งนี้:
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyFirstThread thread = new MyFirstThread();
thread.run();
}
}
}
ดูผลลัพธ์ของโปรแกรมของเรา: เธรดที่ดำเนินการ: เธรด-0 เธรดที่ดำเนินการ: เธรด-1 เธรดที่ดำเนินการ: เธรด-2 เธรดที่ดำเนินการ: เธรด-3 เธรดที่ดำเนินการ: เธรด-4 เธรดที่ดำเนินการ: เธรด-5 เธรดที่ดำเนินการ: เธรด-6 เธรดที่ดำเนินการ: เธรด-7 เธรดที่ดำเนินการ: เธรด-8 เธรดที่ดำเนินการ: เธรด-9 ดูลำดับของเอาต์พุต: ทุกอย่างเกิดขึ้นตามลำดับที่สมบูรณ์แบบ แปลกเหรอ? เราไม่คุ้นเคยกับสิ่งนี้ เพราะเรารู้อยู่แล้วว่าลำดับที่เธรดเริ่มต้นและดำเนินการนั้นถูกกำหนดโดยสติปัญญาที่เหนือกว่าภายในระบบปฏิบัติการของเรา ซึ่งก็คือตัวกำหนดตารางเวลาเธรด บางทีเราอาจจะโชคดี? แน่นอนว่ามันไม่เกี่ยวกับโชค คุณสามารถตรวจสอบได้โดยการเรียกใช้โปรแกรมอีกสองสามครั้ง ประเด็นอยู่ที่การโทรrun()
วิธีการโดยตรงไม่มีส่วนเกี่ยวข้องกับมัลติเธรด ในกรณีนี้ โปรแกรมจะดำเนินการบนเธรดหลัก ซึ่งเป็นเธรดเดียวกับที่เรียกใช้main()
เมธอด มันพิมพ์ 10 บรรทัดบนคอนโซลอย่างต่อเนื่อง แค่นั้น 10 กระทู้ยังไม่ได้เริ่ม ดังนั้นจำสิ่งนี้ไว้ในอนาคตและตรวจสอบตัวเองอยู่เสมอ หากต้องการrun()
ให้เรียกเมธอด ให้start()
เรียก ไปต่อกันเถอะ
วิธีการ Thread.sleep()
หากต้องการระงับการดำเนินการของเธรดปัจจุบันชั่วขณะ เราใช้sleep()
วิธีการ เมธอดsleep()
ใช้เวลาเป็นมิลลิวินาทีเป็นอาร์กิวเมนต์ ซึ่งระบุระยะเวลาที่จะทำให้เธรดเข้าสู่โหมดสลีป
public class Main {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
Thread.sleep(3000);
System.out.println(" - How long did I sleep? \n - " + ((System.currentTimeMillis()-start)) / 1000 + " seconds");
}
}
เอาต์พุตคอนโซล: - ฉันหลับไปนานแค่ไหน? - 3 วินาที หมายเหตุ:วิธีsleep()
การเป็นแบบคงที่: จะนอนหลับเธรดปัจจุบัน นั่นคือผู้ที่กำลังดำเนินการอยู่ นี่เป็นจุดสำคัญอีกประการหนึ่ง: เธรดการนอนหลับสามารถถูกขัดจังหวะได้ ในกรณีนี้ โปรแกรมจะส่งไฟล์InterruptedException
. เราจะพิจารณาตัวอย่างด้านล่าง เกิดอะไรขึ้นหลังจากเธรดตื่นขึ้นมา? มันจะถูกดำเนินการต่อจากที่ค้างไว้หรือไม่? ไม่ หลังจากเธรดกลับมาทำงาน เช่น เวลาผ่านไปเป็นอาร์กิวเมนต์ผ่านไป เธรดThread.sleep()
จะเปลี่ยนเป็นรันได้สถานะ. แต่ไม่ได้หมายความว่าตัวกำหนดตารางเวลาของเธรดจะทำงาน มันอาจค่อนข้างที่จะให้ความสำคัญกับเธรดที่ไม่หลับอื่น ๆ และอนุญาตให้เธรดที่เพิ่งตื่นใหม่ของเราทำงานต่อในภายหลัง อย่าลืมสิ่งนี้: การตื่นนอนไม่ได้หมายความว่าต้องทำงานต่อทันที!
วิธีการ Thread.join()
เมธอดjoin()
จะระงับการดำเนินการของเธรดปัจจุบันจนกว่าเธรดอื่นจะเสร็จสิ้น ถ้าเรามี 2 เธรดt1
และt2
และ เราเขียน
t1.join()
แล้วt2
จะไม่เริ่มจนกว่าt1
จะเสร็จงาน วิธีการ นี้join()
สามารถใช้เพื่อรับประกันลำดับการดำเนินการของเธรด ลองพิจารณาวิธีjoin()
การทำงานในตัวอย่างต่อไปนี้:
public class ThreadExample extends Thread {
@Override
public void run() {
System.out.println("Thread started: " + getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread " + getName() + " is finished.");
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
ThreadExample t1 = new ThreadExample();
ThreadExample t2 = new ThreadExample();
t1.start();
/* The second thread (t2) will start running only after the first thread (t1)
is finished (or an exception is thrown) */
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
// The main thread will continue running only after t1 and t2 have finished
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All threads have finished. The program is finished.");
}
}
เราสร้างThreadExample
คลาส ง่ายๆ หน้าที่ของมันคือการแสดงข้อความว่าเธรดได้เริ่มต้นแล้ว เข้าสู่โหมดสลีปเป็นเวลา 5 วินาที จากนั้นจึงรายงานว่างานเสร็จสมบูรณ์ในที่สุด ชิ้นเค้ก ตรรกะหลักอยู่ในMain
ชั้นเรียน ดูความคิดเห็น: เราใช้join()
วิธีการในการจัดการคำสั่งการดำเนินการของเธรดให้สำเร็จ หากคุณจำได้ว่าเราเริ่มต้นหัวข้อนี้อย่างไร คำสั่งดำเนินการจะถูกจัดการโดยตัวกำหนดตารางเวลาของเธรด มันรันเธรดตามที่เห็นสมควร: แต่ละครั้งด้วยวิธีที่แตกต่างกัน ที่นี่เรากำลังใช้วิธีการเพื่อรับประกันว่าt1
เธรดจะเริ่มต้นและดำเนินการก่อน จากนั้นจึงt2
เธรดและหลังจากนั้นเธรดหลักของโปรแกรมจะดำเนินต่อไป กำลังเดินทางไป. ในโปรแกรมจริง คุณมักจะพบสถานการณ์ที่คุณจำเป็นต้องขัดจังหวะการทำงานของเธรด ตัวอย่างเช่น เธรดของเรากำลังทำงานอยู่ แต่กำลังรอเหตุการณ์หรือเงื่อนไขบางอย่าง หากเกิดขึ้น เธรดจะหยุดทำงาน มันอาจจะสมเหตุสมผลถ้ามีstop()
วิธีการ บางอย่าง แต่มันไม่ง่ายอย่างนั้น กาลครั้งหนึ่ง Java มีThread.stop()
วิธีการและอนุญาตให้เธรดถูกขัดจังหวะ แต่ภายหลังถูกลบออกจากไลบรารี Java คุณสามารถค้นหาได้ในเอกสาร Oracle และเห็นว่ามีการทำเครื่องหมายว่าเลิกใช้แล้ว. ทำไม เพราะมันหยุดเธรดโดยไม่ได้ทำอะไรอีก ตัวอย่างเช่น เธรดอาจทำงานกับข้อมูลและเปลี่ยนแปลงบางอย่าง จากนั้นในระหว่างการทำงานก็ถูกตัดขาดโดยstop()
วิธีการ อย่างกะทันหันและไม่เป็นทางการ หากไม่มีการปิดระบบที่เหมาะสม ไม่มีการเผยแพร่ทรัพยากร ไม่มีแม้แต่การจัดการข้อผิดพลาด — ไม่มีสิ่งนี้ หากต้องการพูดเกินจริงเล็กน้อยstop()
วิธีการนี้ก็ทำลายทุกสิ่งที่ขวางหน้า เหมือนกับการดึงสายไฟออกจากเต้ารับเพื่อปิดคอมพิวเตอร์ ใช่ คุณจะได้ผลลัพธ์ที่ต้องการ แต่ทุกคนรู้ดีว่าหลังจากผ่านไปสองสามสัปดาห์ คอมพิวเตอร์จะไม่ขอบคุณคุณที่ทำแบบนั้น นั่นเป็นสาเหตุที่ตรรกะสำหรับการขัดจังหวะเธรดเปลี่ยนไปใน Java และตอนนี้ใช้interrupt()
วิธี พิเศษ
วิธีการ Thread.interrupt()
จะเกิดอะไรขึ้นถ้าinterrupt()
เมธอดถูกเรียกใช้บนเธรด มี 2 ความเป็นไปได้:
- หากอ็อบเจกต์อยู่ในสถานะรอ ตัวอย่างเช่น เนื่องจาก
join
หรือsleep
เมธอด การรอจะถูกขัดจังหวะและโปรแกรมจะส่งไฟล์InterruptedException
. - หากเธรดอยู่ในสถานะทำงาน แฟ
interrupted
ล็กบูลีนจะถูกตั้งค่าบนอ็อบเจ็กต์
Thread
ชั้นเรียนมีboolean isInterrupted()
วิธีการ กลับไปที่ตัวอย่างนาฬิกาที่อยู่ในบทเรียนในหลักสูตรพื้นฐาน เพื่อความสะดวก เราได้ทำให้ง่ายขึ้นเล็กน้อย:
public class Clock extends Thread {
public static void main(String[] args) throws InterruptedException {
Clock clock = new Clock();
clock.start();
Thread.sleep(10000);
clock.interrupt();
}
public void run() {
Thread current = Thread.currentThread();
while (!current.isInterrupted())
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("The thread was interrupted");
break;
}
System.out.println("Tick");
}
}
}
ในกรณีนี้ นาฬิกาจะเริ่มต้นและเริ่มฟ้องทุกๆ วินาที ในวินาทีที่ 10 เราขัดจังหวะของนาฬิกา ดังที่คุณทราบแล้ว หากเธรดที่เราพยายามขัดจังหวะอยู่ในสถานะรออย่างใดอย่างหนึ่ง ผลลัพธ์InterruptedException
คือ นี่เป็นข้อยกเว้นที่ตรวจสอบแล้ว ดังนั้นเราจึงสามารถตรวจจับได้ง่ายและดำเนินการตามตรรกะของเราเพื่อสิ้นสุดโปรแกรม และนั่นเป็นเพียงสิ่งที่เราทำ นี่คือผลลัพธ์ของเรา: ติ๊ก ติ๊ก ติ๊ก ติ๊ก ติ๊ก ติ๊ก ติ๊ก ติ๊ก ติ๊ก เธรดถูกขัดจังหวะ นี่เป็นการสรุปการแนะนำThread
วิธีการที่สำคัญที่สุดของชั้นเรียน ขอให้โชคดี!
GO TO FULL VERSION