Java闸门计数放行程序:如何实现排队顺序锁公平性?
闸门计数程序的公平性实现及Semaphore版本分析
一、改写Synchronized版本实现公平性
原代码使用的synchronized内置锁默认是非公平的,无法保证等待线程的获取顺序——对应业务场景就是先排队的人可能无法优先进入。要实现公平性,需要改用显式公平锁ReentrantLock(true),它会严格按照线程等待的FIFO顺序分配锁,从而保障排队人员的顺序公平性。
修正后的代码:
import java.util.concurrent.locks.ReentrantLock; public class LineCount { private int totalInLine = 0; // 初始化公平锁,保证等待线程按先后顺序获取锁 private final ReentrantLock fairLock = new ReentrantLock(true); public void addToLine() { fairLock.lock(); try { totalInLine++; if (totalInLine == 5) { System.out.println("People may enter"); totalInLine = 0; } } finally { fairLock.unlock(); } } }
二、现有Semaphore版本的问题及修正
你提供的Semaphore实现存在几个致命问题:
- 重新创建Semaphore导致线程永久阻塞:当执行
semaphore = new Semaphore(5, true)时,已经调用acquire()但未完成操作的线程会持有旧的Semaphore实例,后续无法被唤醒,造成永久等待。 availablePermits()判断非线程安全:多线程环境下,线程A调用acquire()后,线程B可能立刻也完成acquire(),此时线程A判断availablePermits() == 0的结果已经失效,会导致逻辑混乱(比如重复输出提示)。- 冗余操作无意义:释放全部许可后重新创建Semaphore完全没必要,反而破坏了Semaphore的状态一致性。
修正后的Semaphore版本(兼顾公平性和业务逻辑):
import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicInteger; public class LineCount { // 公平Semaphore,保证线程按排队顺序获取许可 private final Semaphore semaphore = new Semaphore(5, true); // 原子计数器,安全统计排队人数 private final AtomicInteger lineCount = new AtomicInteger(0); public void addToLine() throws InterruptedException { semaphore.acquire(); try { int current = lineCount.incrementAndGet(); if (current == 5) { System.out.println("People may enter"); lineCount.set(0); // 释放所有许可,允许下一批5人排队 semaphore.release(5); } } catch (Exception e) { // 异常时释放当前许可,避免许可泄漏 semaphore.release(); throw e; } } }
修正逻辑说明:
- 用
AtomicInteger保证计数操作的原子性,避免多线程下的计数错误; - 公平Semaphore确保线程按FIFO顺序获取许可,对应业务场景中排队人员的先后顺序;
- 不再重新创建Semaphore实例,维持Semaphore状态的一致性;
- 增加异常处理,防止许可泄漏导致后续线程无法获取许可。
内容的提问来源于stack exchange,提问作者mattsmith5
相关产品推荐
相关产品推荐

