如何让Semaphore按自定义优先级唤醒等待线程?
自定义优先级的Semaphore实现方案
你这个需求其实是要打破Semaphore默认的FIFO(公平模式)或随机唤醒(非公平模式)逻辑,换成按自定义优先级来选择唤醒线程的策略对吧?默认Semaphore的内部队列机制没法直接修改,所以我们得自己封装一个支持优先级的许可控制类,核心思路是用优先级队列来管理等待的线程,释放许可时优先唤醒最高优先级的线程。
具体实现步骤与代码示例
1. 封装带优先级的等待线程对象
首先我们需要一个类来包装线程和它的优先级,同时实现排序规则,让优先级高的线程能被优先取出:
static class PriorityThread implements Comparable<PriorityThread> { private final Thread thread; private final int priority; public PriorityThread(Thread thread, int priority) { this.thread = thread; this.priority = priority; } @Override public int compareTo(PriorityThread o) { // 这里定义优先级规则:数值越大,优先级越高 // 如果需要数值小的优先级高,改成Integer.compare(this.priority, o.priority)即可 return Integer.compare(o.priority, this.priority); } public Thread getThread() { return thread; } }
2. 实现自定义的PrioritySemaphore类
这个类会管理许可数量,用线程安全的优先级队列维护等待线程,同时用锁保证操作的线程安全:
class PrioritySemaphore { private final int totalPermits; private int availablePermits; private final ReentrantLock lock = new ReentrantLock(); private final PriorityBlockingQueue<PriorityThread> waitQueue = new PriorityBlockingQueue<>(); public PrioritySemaphore(int totalPermits) { this.totalPermits = totalPermits; this.availablePermits = totalPermits; } // 带优先级的获取许可方法 public void acquire(int priority) throws InterruptedException { lock.lock(); try { // 如果有可用许可,直接获取 if (availablePermits > 0) { availablePermits--; return; } // 没有许可,把当前线程包装后加入优先级队列 PriorityThread currentPT = new PriorityThread(Thread.currentThread(), priority); waitQueue.add(currentPT); // 释放锁后挂起线程,避免占用锁导致其他线程无法操作 lock.unlock(); synchronized (currentPT) { currentPT.wait(); } // 被唤醒后重新获取锁,继续后续流程 lock.lock(); } finally { lock.unlock(); } } // 释放许可方法 public void release() { lock.lock(); try { availablePermits++; // 取出优先级最高的等待线程并唤醒 PriorityThread highestPT = waitQueue.poll(); if (highestPT != null) { synchronized (highestPT) { highestPT.notify(); } } } finally { lock.unlock(); } } // 可选:获取当前可用许可数 public int availablePermits() { lock.lock(); try { return availablePermits; } finally { lock.unlock(); } } }
3. 在你的业务类中使用PrioritySemaphore
修改你的MyMathUtil2类,加入优先级字段,替换原有的Semaphore为我们自定义的PrioritySemaphore:
class MyMathUtil2 implements Runnable { double a; double b; String name; int threadPriority; // 新增线程优先级字段 Thread t; private static final int MAX_COUNT = 2; // 替换为自定义的优先级Semaphore private static final PrioritySemaphore available = new PrioritySemaphore(MAX_COUNT); public MyMathUtil2(double a, double b, String name, int threadPriority) { this.a = a; this.b = b; this.name = name; this.threadPriority = threadPriority; this.t = new Thread(this, name); t.start(); } @Override public void run() { try { // 传入优先级获取许可 available.acquire(threadPriority); System.out.printf("线程【%s】开始执行,优先级:%d%n", name, threadPriority); // 模拟业务操作(比如计算、IO等) Thread.sleep(2000); double result = a + b; System.out.printf("线程【%s】执行完成,计算结果:%.1f%n", name, result); } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.out.printf("线程【%s】被中断%n", name); } finally { // 释放许可 available.release(); System.out.printf("线程【%s】释放许可%n", name); } } // 测试用例 public static void main(String[] args) { // 创建不同优先级的线程,观察执行顺序 new MyMathUtil2(1.0, 2.0, "低优先级线程", 1); new MyMathUtil2(3.0, 4.0, "高优先级线程", 10); new MyMathUtil2(5.0, 6.0, "中优先级线程", 5); new MyMathUtil2(7.0, 8.0, "最高优先级线程", 15); } }
实现关键点说明
- 优先级队列的线程安全:用
PriorityBlockingQueue来维护等待线程,它本身是线程安全的,不需要额外加锁保护队列的入队/出队操作,但许可计数的修改还是需要锁来保证原子性。 - 线程挂起与唤醒:每个等待线程通过自己的
PriorityThread对象作为锁来挂起/唤醒,避免了全局锁的竞争,同时保证唤醒的准确性。 - 优先级规则可定制:在
PriorityThread的compareTo方法里可以自由调整优先级的排序逻辑,比如改成按业务优先级、任务类型等排序,完全适配你的需求。
内容的提问来源于stack exchange,提问作者ritesh
相关产品推荐
相关产品推荐

