Loading

Semaphore源码阅读

本人的源码阅读主要聚焦于类的使用场景,一般只在java层面进行分析,没有深入到一些native方法的实现。并且由于知识储备不完整,很可能出现疏漏甚至是谬误,欢迎指出共同学习

本文基于corretto-17.0.9源码,参考本文时请打开相应的源码对照,否则你会不知道我在说什么

简介

semaphore翻译过来即信号量,对semaphore最直观的解释就是,信号量是一个令牌桶,如果桶里的令牌数>=线程想要获取的令牌数,那么线程就能获取到令牌,然后继续执行,否则就要阻塞等待其他线程将令牌放入桶里,直到桶里令牌数足够,非常简单。

不像JUC里的Lock,线程只能释放自己获得的锁,即Lock有owner的概念,不能随便释放其他人的锁。而semaphore中令牌没有owner的概念,任何线程都能随意往桶里放令牌。

代码分析

成员变量

public class Semaphore implements java.io.Serializable {
  private final Sync sync;
}

成员变量非常简单只有一个,看到Sync就该知道这个是AQS了。信号量还支持fairness,因此Sync作为抽象基类,实现了NonFairSync和FairSync。

方法

Sync

Sync继承自AQS,state定义为桶中令牌数量。由于可能同时有多个线程获得令牌,自然需要重写AQS的两个方法:

  • tryAcquireShared
  • tryReleaseShared

在tryAcquire这里需要区分公平与否,在之前的文章说过,公平的tryAcquire通过检查是否有前驱等待者,即下面两个条件满足一个都属于有前驱等待者,那么就直接tryAcquire失败:

  • 当前线程没进入阻塞队列,且队列不为空
  • 当前线程已经在阻塞队列中,但前面有更靠近队头(等待时间更长)的线程

否则如果是非公平的,就直接尝试更改state即可。

abstract static class Sync extends AbstractQueuedSynchronizer {
  // 初始化state为令牌数量
  Sync(int permits) {
    setState(permits);
  }

  final int getPermits() {
    return getState();
  }

  // nonfair版本的tryAcquireShared,如果目前令牌足够,直接更改令牌数(state)即可
  final int nonfairTryAcquireShared(int acquires) {
    for (;;) {
      int available = getState();
      int remaining = available - acquires;
      if (remaining < 0 ||
        compareAndSetState(available, remaining))
        return remaining;
    }
  }

  // 往桶里放令牌,直接更改state
  protected final boolean tryReleaseShared(int releases) {
    for (;;) {
      int current = getState();
      int next = current + releases;
      if (next < current) // overflow
        throw new Error("Maximum permit count exceeded");
      if (compareAndSetState(current, next))
        return true;
    }
  }
  
  // 忽略其他非核心方法
}

NonFairSync

static final class NonfairSync extends Sync {
  NonfairSync(int permits) {
    super(permits);
  }

  // 非公平tryAcquire
  protected int tryAcquireShared(int acquires) {
    return nonfairTryAcquireShared(acquires);
  }
}

FairSync

static final class FairSync extends Sync {
  private static final long serialVersionUID = 2014338818796000944L;

  FairSync(int permits) {
    super(permits);
  }

  // 公平tryAcquire
  protected int tryAcquireShared(int acquires) {
    for (;;) {
      // 检查是否有阻塞更久的线程
      if (hasQueuedPredecessors())
        return -1;
      int available = getState();
      int remaining = available - acquires;
      if (remaining < 0 || compareAndSetState(available, remaining))
        return remaining;
    }
  }
}
posted @ 2024-01-31 14:26  NOSAE  阅读(12)  评论(0编辑  收藏  举报