คำนำ. ลุงเพชร
สมมุติว่าเราต้องการเติมน้ำหนึ่งขวด มีขวดและน้ำประปาของลุงเพชรไว้ให้บริการ วันนี้ลุงเพชรยาได้ติดตั้ง faucet ใหม่ และเขาก็ชื่นชมความสวยงามของมันอยู่เสมอ ก่อนหน้านั้นเขาใช้แค่ก๊อกน้ำเก่าที่อุดตัน ดังนั้นเส้นขวดจึงใหญ่มาก หลังจากอึกอักเล็กน้อยก็ได้ยินเสียงเติมน้ำจากทิศทางที่หก ผ่านไป 2 นาทีขวดยังอยู่ในขั้นตอนการเติม คิวปกติมารวมตัวกันข้างหลังเรา และภาพในหัว คือความห่วงใยของลุง Petya คัดสรรเฉพาะโมเลกุล H2O ที่ดีที่สุดมาไว้ในขวดของเรา ลุง Petya ซึ่งได้รับการฝึกฝนจากชีวิตสงบจิตใจคนก้าวร้าวโดยเฉพาะและสัญญาว่าจะทำให้เสร็จโดยเร็วที่สุด เมื่อดื่มขวดเสร็จแล้วเขาก็หยิบขวดต่อไปแล้วเปิดแรงดันตามปกติซึ่งไม่ได้เปิดเผยความสามารถทั้งหมดของก๊อกน้ำใหม่ คนไม่มีความสุข...
ทฤษฎี
มัลติเธรดคือความสามารถของแพลตฟอร์มในการสร้างหลายเธรดภายในกระบวนการเดียว การสร้างและดำเนินการเธรดนั้นง่ายกว่าการสร้างกระบวนการมาก ดังนั้นหากจำเป็นต้องใช้การดำเนินการแบบขนานหลายรายการในโปรแกรมเดียว ก็จะมีการใช้เธรดเพิ่มเติม ใน JVM โปรแกรมใดๆ ก็ตามจะรันในเธรดหลัก และส่วนที่เหลือจะถูกเรียกใช้จากเธรดหลัก ภายในกระบวนการเดียวกัน เธรดสามารถแลกเปลี่ยนข้อมูลระหว่างกันได้ เมื่อเริ่มเธรดใหม่ คุณสามารถประกาศให้เป็นเธรดผู้ใช้โดยใช้เมธอด
setDaemon(true);
เธรดดังกล่าวจะยุติโดยอัตโนมัติหากไม่มีเธรดอื่นที่ทำงานอยู่เหลืออยู่ เธรดมีลำดับความสำคัญในการทำงาน (การเลือกลำดับความสำคัญไม่ได้รับประกันว่าเธรดที่มีลำดับความสำคัญสูงสุดจะเสร็จเร็วกว่าเธรดที่มีลำดับความสำคัญต่ำกว่า)
- MIN_PRIORITY
- NORM_PRIORITY (ค่าเริ่มต้น)
- MAX_PRIORITY
วิธีการพื้นฐานเมื่อทำงานกับสตรีม:
run()
– ดำเนินการเธรด
start()
– เริ่มกระทู้
getName()
– ส่งคืนชื่อเธรด
setName()
– ระบุชื่อของสตรีม
wait()
– วิธีการสืบทอด เธรดจะรอให้เรียกวิธีการnotify()
จากเธรดอื่น
notify()
– วิธีการสืบทอด ดำเนินต่อเธรดที่หยุดไปก่อนหน้านี้
notifyAll()
– วิธีการสืบทอด ดำเนินต่อเธรดที่หยุดไปก่อนหน้านี้
sleep()
– หยุดการสตรีมชั่วคราวตามเวลาที่กำหนด
join()
– รอให้เธรดเสร็จสมบูรณ์
interrupt()
– ขัดจังหวะการทำงานของเธรด
สามารถดูวิธีการเพิ่มเติมได้
ที่นี่ ถึงเวลาคิดเกี่ยวกับเธรดใหม่หากโปรแกรมของคุณมี:
- การเข้าถึงเครือข่าย
- การเข้าถึงระบบไฟล์
- กุย
คลาสเธรด
เธรดใน Java แสดงเป็นคลาส
Thread
และคลาสสืบทอด ตัวอย่างด้านล่างนี้คือการใช้งานคลาสสตรีมอย่างง่าย
import static java.lang.System.out;
public class ExampleThread extends Thread{
public static void main(String[] args) {
out.println("Основной поток");
new ExampleThread().start();
}
@Override
public void run() {
out.println("Новый поток");
}
}
เป็นผลให้เราได้รับ
Основной поток
Новый поток
ที่นี่เราสร้างคลาสของเราและทำให้มันเป็นคลาสลูกหลานของคลาส
Thread
หลังจากนั้นเราจะเขียนเมธอด main() เพื่อเรียกใช้เธรดหลักและแทนที่เมธอด
run()
คลาส
Thread
ตอนนี้ เมื่อสร้างอินสแตนซ์ของคลาสของเราและดำเนินการวิธีการที่สืบทอดมาเรา
start()
จะเปิดตัวเธรดใหม่ซึ่งทุกสิ่งที่อธิบายไว้ในเนื้อความของวิธีการจะถูกดำเนินการ
run()
ฟังดูซับซ้อน แต่เมื่อดูโค้ดตัวอย่างแล้ว ทุกอย่างควรจะชัดเจน
อินเทอร์เฟซที่รันได้
ออราเคิลยังแนะนำให้ใช้อินเทอร์เฟซเพื่อเริ่มเธรดใหม่
Runnable
ซึ่งให้ความยืดหยุ่นในการออกแบบมากกว่าการสืบทอดที่มีอยู่ในตัวอย่างก่อนหน้าเท่านั้น (หากคุณดูที่แหล่งที่มาของคลาส
Thread
คุณจะเห็นว่ามีการใช้อินเทอร์เฟซด้วย
Runnable
) ลองใช้วิธีที่แนะนำในการสร้างเธรดใหม่
import static java.lang.System.out;
public class ExampleRunnable implements Runnable {
public static void main(String[] args) {
out.println("Основной поток");
new Thread(new ExampleRunnable()).start();
}
@Override
public void run() {
out.println("Новый поток");
}
}
เป็นผลให้เราได้รับ
Основной поток
Новый поток
ตัวอย่างจะคล้ายกันมากเพราะว่า เมื่อเขียนโค้ด เราต้องใช้วิธีนามธรรม
run()
ที่อธิบายไว้ในอินเทอร์เฟ
Runnable
ซ การเปิดกระทู้ใหม่จะแตกต่างออกไปเล็กน้อย เราสร้างอินสแตนซ์ของคลาส
Thread
โดยส่งการอ้างอิงไปยังอินสแตนซ์ของการใช้งานอินเทอร์เฟซของเราเป็น
Runnable
พารามิเตอร์ เป็นแนวทางนี้ที่ช่วยให้คุณสามารถสร้างเธรดใหม่โดยไม่ต้องสืบทอดคลาส
Thread
โดยตรง
การดำเนินงานที่ยาวนาน
ตัวอย่างต่อไปนี้จะแสดงประโยชน์ของการใช้หลายเธรดอย่างชัดเจน สมมติว่าเรามีงานง่ายๆ ที่ต้องใช้การคำนวณที่กินเวลานานหลายครั้ง ก่อนที่บทความนี้เราจะแก้มันด้วยวิธีการ
main()
บางทีอาจแบ่งมันออกเป็นวิธีแยกกันเพื่อความสะดวกในการรับรู้ แม้กระทั่งคลาสด้วยซ้ำ แต่สาระสำคัญจะเหมือนกัน การดำเนินการทั้งหมดจะดำเนินการตามลำดับทีละรายการ มาจำลองการคำนวณจำนวนมากและวัดเวลาดำเนินการกัน
public class ComputeClass {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete 1");
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete 2");
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete 3");
long timeSpent = System.currentTimeMillis() - startTime;
System.out.println("программа выполнялась " + timeSpent + " миллисекунд");
}
}
เป็นผลให้เราได้รับ
complete 1
complete 2
complete 3
программа выполнялась 9885 миллисекунд
เวลาดำเนินการไม่เป็นที่ต้องการมากนักและตลอดเวลานี้เรากำลังดูหน้าจอเอาต์พุตที่ว่างเปล่าและสถานการณ์ก็คล้ายกับเรื่องราวเกี่ยวกับลุง Petya มาก แต่ตอนนี้เราซึ่งเป็นผู้พัฒนาไม่ได้ใช้ประโยชน์จากบทบาทของเขา ความสามารถทั้งหมดของอุปกรณ์ที่ทันสมัย เราจะปรับปรุง
public class ComputeClass {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
new MyThread(1).start();
new MyThread(2).start();
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete 3");
long timeSpent = System.currentTimeMillis() - startTime;
System.out.println("программа выполнялась " + timeSpent + " миллисекунд");
}
}
class MyThread extends Thread{
int n;
MyThread(int n){
this.n = n;
}
@Override
public void run() {
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete " + n);
}
}
เป็นผลให้เราได้รับ
complete 1
complete 2
complete 3
программа выполнялась 3466 миллисекунд
เวลาทำงานลดลงอย่างมาก (ผลกระทบนี้อาจไม่เกิดขึ้นหรืออาจเพิ่มเวลาดำเนินการบนโปรเซสเซอร์ที่ไม่รองรับมัลติเธรด) เป็นที่น่าสังเกตว่าเธรดอาจไม่เป็นระเบียบ และหากนักพัฒนาต้องการความสามารถในการคาดเดาการดำเนินการ เขาจะต้องดำเนินการอย่างอิสระสำหรับกรณีเฉพาะ
กลุ่มกระทู้
เธรดใน Java สามารถรวมเป็นกลุ่มได้ โดยคลาสนี้ใช้สำหรับ
ThreadGroup
. กลุ่มสามารถมีทั้งเธรดเดี่ยวและทั้งกลุ่ม วิธีนี้จะสะดวกถ้าคุณต้องการขัดจังหวะโฟลว์ที่เกี่ยวข้อง เช่น กับเครือข่ายเมื่อการเชื่อมต่อขาดหาย คุณสามารถอ่านเพิ่มเติมเกี่ยวกับกลุ่ม
ได้ที่นี่ ฉันหวังว่าตอนนี้หัวข้อจะชัดเจนยิ่งขึ้นสำหรับคุณ และผู้ใช้ของคุณจะพึงพอใจ
GO TO FULL VERSION