สวัสดี! วันนี้เรายังคงพูดคุยเกี่ยวกับมัลติเธรด ลองมาดูที่คลาส Thread และวิธีการต่างๆ ของมันทำงานอย่างไร ก่อนหน้านี้ เมื่อเราศึกษา class method เรามักจะเขียนง่ายๆ แบบนี้: “ชื่อของ method” -> “มันทำอะไร”
สิ่งนี้ใช้ไม่ได้กับวิธีการของ Thread :) ตรรกะของพวกเขาซับซ้อนกว่า และหากไม่มีตัวอย่างหลายตัวอย่าง ก็เป็นไปไม่ได้ที่จะเข้าใจ
จำตัวอย่างจากการบรรยายครั้งก่อน:
วิธีการนี้
วิธีการ
มันจะทำหน้าที่เป็นวัสดุเพิ่มเติมที่ยอดเยี่ยม! ท้ายที่สุดหลังจากภาพรวมของวิธีการต่างๆ แล้ว ก็บอกได้อย่างชัดเจนว่าเราจะผ่านอะไรต่อไปในหลักสูตรนี้ :) โชคดีนะ!
วิธีการ Thread.start()
เริ่มต้นด้วยการทำซ้ำ ดังที่คุณคงจำได้ คุณสามารถสร้างเธรดได้โดยการสืบทอดคลาสของคุณจากคลาสThread
และแทนที่เมธอดในคลาสrun()
นั้น แต่แน่นอนว่ามันจะไม่เริ่มต้นด้วยตัวเอง เมื่อต้องการทำสิ่งนี้ เราเรียกเมธอดบนอ็อบเจ็กต์ของstart()
เรา 
public class MyFirstThread extends Thread {
@Override
public void run() {
System.out.println("Выполнен поток " + 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()
! นี่เป็นข้อผิดพลาดง่าย ๆ โดยเฉพาะอย่างยิ่งเมื่อเรียนรู้มัลติเธรดครั้งแรก หากในตัวอย่างของเรา คุณเรียกใช้เมธอดบนอ็อบเจ็กต์ 10 ครั้งrun()
แทนที่จะเป็น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 ดำเนินการเธรด เธรด-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(" - Сколько я проспал? \n - " + ((System.currentTimeMillis()-start)) / 1000 + " секунды");
}
}
เอาต์พุตคอนโซล: - ฉันนอนนานแค่ไหน? - 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("Начало работы потока " + getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Поток " + getName() + " завершил работу.");
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
ThreadExample t1 = new ThreadExample();
ThreadExample t2 = new ThreadExample();
t1.start();
/*Второй поток t2 начнет выполнение только после того, How будет завершен
(or бросит исключение) первый поток - t1*/
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
//Главный поток продолжит работу только после того, How t1 и t2 завершат работу
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Все потоки закончor работу, программа завершена");
}
}
เราได้สร้างคลาสที่เรียบThreadExample
ง่าย หน้าที่คือแสดงข้อความเกี่ยวกับการเริ่มงานบนหน้าจอ จากนั้นหลับไป 5 วินาที และแจ้งเกี่ยวกับความสำเร็จของงานในตอนท้าย ไม่มีอะไรซับซ้อน Main
ตรรกะหลัก มีอยู่ในคลาส ดูความคิดเห็น: เมื่อใช้วิธีนี้join()
เราจะควบคุมลำดับการดำเนินการของเธรดได้สำเร็จ หากคุณจำจุดเริ่มต้นของหัวข้อได้ ตัวกำหนดเวลาเธรดจะทำสิ่งนี้ เขาเปิดตัวสิ่งเหล่านี้ตามดุลยพินิจของเขาเอง: แตกต่างกันในแต่ละครั้ง โดยใช้วิธีนี้ เรารับรองว่าเธรดแรกจะถูกเรียกใช้และดำเนินการt1
จากนั้นเธรดt2
และหลังจากนั้นเธรดหลักของการทำงานของโปรแกรมเท่านั้น ไปข้างหน้า. ในโปรแกรมจริง คุณมักจะเผชิญกับสถานการณ์เมื่อคุณต้องการขัดจังหวะการทำงานของเธรดบางเธรด ตัวอย่างเช่น เธรดของเรากำลังทำงานอยู่ แต่กำลังรอให้มีเหตุการณ์หรือเงื่อนไขบางอย่างเกิดขึ้น หากสิ่งนี้เกิดขึ้นก็จะหยุดลง มันคงจะสมเหตุสมผลถ้ามีวิธีการบางอย่างstop()
เช่น อย่างไรก็ตามทุกอย่างไม่ง่ายนัก กาลครั้งหนึ่งThread.stop()
มีวิธีการใน Java จริง ๆ และอนุญาตให้คุณขัดจังหวะการทำงานของเธรดได้ แต่ต่อมามันถูกลบออกจากไลบรารี Java คุณสามารถค้นหาได้ในเอกสารของ Oracle และดูว่ามีการทำเครื่องหมายว่าเลิกใช้แล้ว ทำไม เพราะมันแค่หยุดโฟลว์โดยไม่ต้องทำงานเพิ่มเติม ตัวอย่างเช่น เธรดสามารถทำงานกับข้อมูลและเปลี่ยนแปลงบางอย่างในนั้นได้ จากนั้นเขาก็ถูกกระแทกstop()
กลางงานอย่างกะทันหัน - แค่นั้นเอง ไม่มีการปิดระบบที่ถูกต้อง ไม่มีการเพิ่มทรัพยากร ไม่มีแม้แต่การจัดการข้อผิดพลาด - ไม่มีสิ่งใดเกิดขึ้น วิธีการstop()
พูดเกินจริงเพียงทำลายทุกสิ่งที่ขวางหน้า การทำงานของมันสามารถเปรียบเทียบได้กับการดึงปลั๊กออกจากเต้ารับเพื่อปิดคอมพิวเตอร์ ใช่ คุณสามารถบรรลุผลตามที่ต้องการได้ แต่ทุกคนเข้าใจดีว่าในอีกไม่กี่สัปดาห์คอมพิวเตอร์จะไม่พูดว่า "ขอบคุณ" สำหรับสิ่งนี้ ด้วยเหตุนี้ ตรรกะในการขัดจังหวะเธรดใน Java จึงเปลี่ยนไป และตอนนี้มีการใช้วิธีพิเศษinterrupt()
-
Thread.interrupt() วิธีการ
จะเกิดอะไรขึ้นถ้าคุณเรียกใช้เมธอดinterrupt()
บนเธรด ? มี 2 ตัวเลือก:
- หากอ็อบเจ็กต์อยู่ในสถานะรอในขณะนั้น หรือ
join
การsleep
รอจะถูกขัดจังหวะและโปรแกรมจะInterruptedException
Throw - หากเธรดอยู่ในสถานะใช้งานได้ในขณะนั้น ธงบูลีนของวัตถุจะถูกตั้ง
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("Работа потока была прервана");
break;
}
System.out.println("Tik");
}
}
}
ในกรณีของเรา นาฬิกาเริ่มเดินและเริ่มเดินทุกวินาที ในวินาทีที่ 10 เราขัดจังหวะการไหลของนาฬิกา ดังที่คุณทราบอยู่แล้วว่า หากเธรดที่เราพยายามขัดจังหวะอยู่ในสถานะรออย่างใดอย่างหนึ่ง ผลลัพธ์ที่ได้คือInterruptedException
. ข้อยกเว้นประเภทนี้เป็นข้อยกเว้นที่ได้รับการตรวจสอบ ดังนั้นจึงสามารถตรวจจับได้ง่ายและดำเนินการตรรกะการยกเลิกโปรแกรมของเรา นั่นคือสิ่งที่เราทำ นี่คือผลลัพธ์ของเรา: Tik Tik Tik Tik Tik Tik Tik Tik Tik งานของเธรดถูกขัดจังหวะ นี่เป็นการสรุปการแนะนำวิธีการหลักของชั้นThread
เรียน เพื่อรวบรวมความรู้ของคุณ คุณสามารถชมวิดีโอบรรยายเกี่ยวกับมัลติเธรดนี้:
GO TO FULL VERSION