พูลเธรดอีกประเภทหนึ่งคือ "แคช" พูลเธรดดังกล่าวใช้กันทั่วไปเช่นเดียวกับเธรดแบบคงที่
ตามชื่อที่ระบุ เธรดพูลประเภทนี้แคชเธรด มันรักษาเธรดที่ไม่ได้ใช้ให้คงอยู่ในช่วงเวลาจำกัด เพื่อนำเธรดเหล่านั้นกลับมาใช้ใหม่เพื่อทำงานใหม่ กลุ่มเธรดดังกล่าวดีที่สุดเมื่อเรามีงานเบาพอสมควร
ความหมายของ "จำนวนที่เหมาะสม" ค่อนข้างกว้าง แต่คุณควรทราบว่ากลุ่มดังกล่าวไม่เหมาะสำหรับทุกงาน ตัวอย่างเช่น สมมติว่าเราต้องการสร้างงานหนึ่งล้านงาน แม้ว่าแต่ละครั้งจะใช้เวลาเพียงเล็กน้อย แต่เราจะยังคงใช้ทรัพยากรในปริมาณที่ไม่สมเหตุสมผลและทำให้ประสิทธิภาพการทำงานลดลง เราควรหลีกเลี่ยงกลุ่มดังกล่าวเมื่อไม่สามารถคาดการณ์เวลาดำเนินการได้ เช่น กับงาน I/O
ภายใต้ประทุน ตัวสร้าง ThreadPoolExecutorถูกเรียกโดยมีอาร์กิวเมนต์ต่อไปนี้:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
ค่าต่อไปนี้จะถูกส่งผ่านไปยังตัวสร้างเป็นอาร์กิวเมนต์:
พารามิเตอร์ | ค่า |
---|---|
corePoolSize (จำนวนเธรดที่จะพร้อม (เริ่มต้น) เมื่อ บริการ ตัวดำเนินการเริ่มทำงาน) | 0 |
maximumPoolSize (จำนวนเธรดสูงสุดที่ บริการ ตัวดำเนินการสามารถสร้างได้) | จำนวนเต็ม MAX_VALUE |
keepAliveTime (เวลาที่เธรดที่ว่างจะยังคงมีชีวิตอยู่ก่อนที่จะถูกทำลายหากจำนวนของเธรดมากกว่าcorePoolSize ) | 60L |
หน่วย (หน่วยของเวลา) | เวลาหน่วยวินาที |
workQueue (การดำเนินการตามคิว) | ใหม่ SynchronousQueue <รันได้>() |
และเราสามารถผ่านการใช้งานThreadFactoryในแบบเดียวกันได้
พูดคุยเกี่ยวกับ SynchronousQueue
แนวคิดพื้นฐานของการถ่ายโอนแบบซิงโครนัสนั้นค่อนข้างเรียบง่ายและสวนทางกับสัญชาตญาณ (นั่นคือสัญชาตญาณหรือสามัญสำนึกบอกคุณว่ามันผิด): คุณสามารถเพิ่มองค์ประกอบลงในคิวได้ก็ต่อเมื่อเธรดอื่นได้รับองค์ประกอบที่ ในเวลาเดียวกัน. กล่าวอีกนัยหนึ่งคิวแบบซิงโครนัสไม่สามารถมีงานอยู่ในนั้น เนื่องจากทันทีที่งานใหม่มาถึง เธรดที่ดำเนินการได้เลือกงานแล้ว

เมื่องานใหม่เข้าสู่คิว หากมีเธรดว่างที่ใช้งานอยู่ในพูล ก็จะรับงานนั้น หากเธรดทั้งหมดไม่ว่าง เธรดใหม่จะถูกสร้างขึ้น
พูลแคชเริ่มต้นด้วยเธรดศูนย์และสามารถขยายเป็นเธรดInteger.MAX_VALUE โดยพื้นฐานแล้ว ขนาดของพูลเธรดแคชจะถูกจำกัดโดยทรัพยากรระบบเท่านั้น
เพื่อรักษาทรัพยากรระบบ แคชเธรดพูลจะลบเธรดที่ไม่ได้ใช้งานเป็นเวลาหนึ่งนาที
มาดูกันว่าได้ผลจริงอย่างไร เราจะสร้างคลาสงานที่จำลองคำขอของผู้ใช้:
public class Task implements Runnable {
int taskNumber;
public Task(int taskNumber) {
this.taskNumber = taskNumber;
}
@Override
public void run() {
System.out.println("Processed user request #" + taskNumber + " on thread " + Thread.currentThread().getName());
}
}
ใน เมธอด หลักเราสร้างnewCachedThreadPoolแล้วเพิ่ม 3 งานสำหรับการดำเนินการ ที่นี่เราพิมพ์สถานะบริการของเรา(1) .
ต่อไป เราหยุดชั่วคราวเป็นเวลา 30 วินาที เริ่มงานอื่น และแสดงสถานะ(2) .
หลังจากนั้น เราหยุดเธรดหลักของเราชั่วคราวเป็นเวลา 70 วินาที พิมพ์สถานะ( 3)จากนั้นเพิ่มงาน 3 งานอีกครั้ง และพิมพ์สถานะอีกครั้ง(4)
ในสถานที่ที่เราแสดงสถานะทันทีหลังจากเพิ่มงาน อันดับแรก เราจะเพิ่มโหมดสลีป 1 วินาที สำหรับเอาต์พุตที่เป็นปัจจุบัน
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 3; i++) {
executorService.submit(new Task(i));
}
TimeUnit.SECONDS.sleep(1);
System.out.println(executorService); //(1)
TimeUnit.SECONDS.sleep(30);
executorService.submit(new Task(3));
TimeUnit.SECONDS.sleep(1);
System.out.println(executorService); //(2)
TimeUnit.SECONDS.sleep(70);
System.out.println(executorService); //(3)
for (int i = 4; i < 7; i++) {
executorService.submit(new Task(i));
}
TimeUnit.SECONDS.sleep(1);
System.out.println(executorService); //(4)
executorService.shutdown();
และนี่คือผลลัพธ์:
คำขอของผู้ใช้ที่ประมวลผล #1 บนเธรด pool-1-thread-2
คำขอของผู้ใช้ที่ประมวลผล #2 บนเธรด pool-1-thread-3
(1) java.util.concurrent .ThreadPoolExecutor@f6f4d33[กำลังทำงาน, ขนาดพูล = 3, เธรดที่ใช้งานอยู่ = 0, งานที่อยู่ในคิว = 0, งานที่เสร็จสมบูรณ์ = 3] คำขอของผู้ใช้ที่ประมวลผล #3 บนเธรด pool-1-thread-2 (
2
) java.util.concurrent ThreadPoolExecutor@f6f4d33[กำลังทำงาน ขนาดพูล = 3 เธรดที่ใช้งานอยู่ = 0 งานที่อยู่ในคิว = 0 งานที่เสร็จสมบูรณ์ = 4] (3) java.util.concurrent.ThreadPoolExecutor@f6f4d33[กำลังทำงาน
ขนาดพูล = 0 เธรดที่ใช้งานอยู่ = 0 , งานที่อยู่ในคิว = 0, งานที่เสร็จสมบูรณ์ = 4]
คำขอของผู้ใช้ที่ประมวลผล #4 บนเธรด pool-1-thread-4
คำขอของผู้ใช้ที่ประมวลผล #5 บนเธรด pool-1-thread-5
คำขอของผู้ใช้ที่ประมวลผล #6 บนเธรด pool-1-thread-4
(4) java.util.concurrent.ThreadPoolExecutor@f6f4d33[กำลังทำงาน, ขนาดพูล = 2, เธรดที่ใช้งานอยู่ = 0, งานที่อยู่ในคิว = 0, งานที่ทำเสร็จแล้ว = 7]
มาดูกันทีละขั้นตอน:
ขั้นตอนที่ | คำอธิบาย |
---|---|
1 (หลังจาก 3 ภารกิจเสร็จสิ้น) | เราสร้าง 3 เธรด และ 3 งานถูกดำเนินการบนเธรดทั้งสามนี้ เมื่อสถานะแสดงขึ้น งานทั้ง 3 อย่างจะเสร็จสิ้น และเธรดพร้อมที่จะทำงานอื่นๆ |
2 (หลังจากหยุดชั่วคราว 30 วินาทีและดำเนินการงานอื่น) | หลังจากไม่มีการใช้งานเป็นเวลา 30 วินาที เธรดจะยังคงอยู่และกำลังรองาน งานอื่นถูกเพิ่มและดำเนินการบนเธรดที่นำมาจากพูลของเธรดที่ใช้งานจริงที่เหลืออยู่ ไม่มีการเพิ่มเธรดใหม่ลงในกลุ่ม |
3 (หลังจากหยุดชั่วคราว 70 วินาที) | เธรดถูกลบออกจากพูล ไม่มีเธรดที่พร้อมจะรับงาน |
4 (หลังจากดำเนินการอีก 3 งาน) | หลังจากได้รับงานเพิ่มเติม เธรดใหม่จะถูกสร้างขึ้น ครั้งนี้มีเพียง 2 เธรดที่จัดการเพื่อประมวลผล 3 งาน |
ตอนนี้คุณคุ้นเคยกับตรรกะของบริการตัวดำเนินการประเภทอื่นแล้ว
ด้วยการเปรียบเทียบกับเมธอดอื่นๆ ของคลาสยูทิลิตี้Executors เมธอด newCachedThreadPoolยังมีเวอร์ชันโอเวอร์โหลดที่ใช้ ออบเจกต์ ThreadFactoryเป็นอาร์กิวเมนต์
GO TO FULL VERSION