Java LongAdder: ลดการแย่ง counter ใน metrics แต่ห้ามใช้แทนเลขลำดับธุรกิจ
พี่โชว์Engineering
counter กลางตัวเดียวที่ทุก thread เพิ่มพร้อมกันอาจกลายเป็นจุดแย่ง cache line ครับ LongAdder กระจายการเพิ่มไปหลาย cell แล้วรวมเมื่อเรียก sum() จึงเหมาะกับ metrics ที่เขียนถี่และอ่านเป็นช่วง
import java.util.concurrent.atomic.LongAdder;
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
LongAdder requests = new LongAdder();
IntStream.range(0, 10_000).parallel().forEach(ignored -> requests.increment());
assert requests.sum() == 10_000L;
long drained = requests.sumThenReset();
assert drained == 10_000L && requests.sum() == 0L;
System.out.println("LongAdder tests passed");
}
}
sum() ไม่ใช่ atomic snapshot ขณะที่มี update พร้อมกัน และ sumThenReset() อาจพลาด update หากใช้โดยไม่มีจุดประสาน จึงไม่เหมาะกับยอดเงิน, inventory, sequence ID หรือ quota ที่ต้องตัดสินใจจากค่าที่แม่นยำ ณ จุดเดียว กรณีนั้นใช้ AtomicLong, lock หรือ transaction ตาม invariant
Recap
- ใช้ LongAdder กับ metric counter ที่ contention สูง
- ยอมรับว่า
sum()เป็นมุมมองระหว่างการอัปเดต - ห้ามใช้กับลำดับหรือยอดที่ต้อง atomic
- benchmark ก่อนแทน AtomicLong ในระบบจริง