Java Semaphore: จำกัดงานพร้อมกันด้วย permit ที่คืนเสมอ
ถ้า service เปิดงานดาวน์โหลดร้อยงานพร้อมกันทั้งที่ฐานข้อมูลรับได้เพียงสิบ connection ปัญหาไม่ใช่ CPU อย่างเดียว แต่คือจำนวนคนที่กรูกันผ่านประตูครับ Semaphore แจก permit ตามจำนวนที่กำหนด งานต้องรับบัตรก่อนเข้าและคืนบัตรเมื่อออก ช่วยคุม concurrent access ที่จุดเดียว
หลักสำคัญคือคืน permit ใน finally หลังได้ permit แล้ว แม้งานข้างในจะโยน exception ไม่เช่นนั้น capacity จะรั่วจนระบบหยุดรับงาน ตัวอย่าง self-contained นี้ทดสอบทั้งกรณีเต็มและคืนแล้วรับใหม่; ระบบจริงต้องวาง code ที่ใช้ resource ไว้ใน block เดียวกัน
import java.util.concurrent.Semaphore;
public class Main {
public static void main(String[] args) throws InterruptedException {
Semaphore slots = new Semaphore(2, true);
slots.acquire();
slots.acquire();
assert slots.availablePermits() == 0;
assert !slots.tryAcquire();
try {
assert slots.isFair();
// งานที่ใช้ทรัพยากรจริงจะอยู่ในขอบเขตนี้
} finally {
slots.release();
slots.release();
}
assert slots.availablePermits() == 2;
assert slots.tryAcquire();
slots.release();
System.out.println("Semaphore tests passed");
}
}
Fairness ไม่ใช่คำสัญญาครอบทุก method
constructor ที่รับ true ขอคิวแบบ FIFO สำหรับการรอ permit เพื่อลด starvation แต่เอกสาร Oracle ระบุว่า tryAcquire() แบบไม่รอสามารถแซงคิวได้แม้ตั้ง fair ถ้าระบบต้องรักษาคิวจริง ให้ดู overload แบบ timed ที่เคารพ fairness และทดสอบภายใต้โหลดจริง นโยบาย fair ยังมีต้นทุน throughput; อย่าเปิดเพียงเพราะชื่อฟังดี
Semaphore จำกัดจำนวนงานที่ เข้า critical section แต่ไม่ได้จำกัดจำนวน request ที่ค้างรอ acquire() ถ้ามี traffic มากขึ้นไม่หยุด ต้องคุมขนาด queue, timeout และนโยบายตอบกลับควบคู่ มิฉะนั้นปัญหาย้ายจาก connection pool ไปเป็นกอง thread ที่รอ permit ครับ
Recap
- กำหนด permit ตาม capacity ของทรัพยากรจริง
-
release()ในfinallyหลังacquire()สำเร็จ - แยก fairness ของ
acquire()กับtryAcquire() - มี timeout หรือ backpressure สำหรับคนที่รอหน้าประตู