Semphore源码解析

    Semphore(信号量)是用来控制同时访问特定资源的线程数量。

    那么它到底是如何实现的呢,让我们点开源码一探究竟吧。

一、属性变量

private final Sync sync;

    属性变量只有一个,那就是Sync类的对象

二、内部类

    1、Sync类

abstract static class Sync extends AbstractQueuedSynchronizer 

    可见Sync类继承与AQS,这个类实现信号量的核心

    2、NonfairSync类

static final class NonfairSync extends Sync

    具有不公平的信号量

    3、FairSync

static final class FairSync extends Sync
    具有公平性的信号量

二、构造方法

public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}

    在只传入信号量个数的情况下,默认创建非公平的信号量

public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

    传入信号量个数并且还传入了boolean类型的变量fair时,如果fiar为true创建公平信号量,不然创建非公平的信号量

三、操作方法

    1、acquire方法

public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}  

    这个方法用来获取同步状态,获取成功线程便可执行临界区的代码,不然则等待其他线程对信号量的释放。

    a、非公平信号量的获取

final int nonfairTryAcquireShared(int acquires) {
    for (;;) {
        int available = getState();
        int remaining = available - acquires;
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}

    将信号量减一,如果可用信号量小于0则返回负数,如果大于0,则使用CAS操作将信号量个数减1

    b、公平信号量的获取

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;
    }
}

        首先调用hashQueuedPredecessors方法,这个方法时判断是否有线程比当前线程更早的请求同步状态。其他步骤和非公平信号量一致。

public final boolean hasQueuedPredecessors() {
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

    如果h不等t,说明同步队列中有多个节点,并且头结点的后继节点如果不是当前线程,说明这个线程比当前更早的请求同步状态,所以需要等待前驱线程获取并释放锁之后才能继续获取锁。

    2、release方法

public void release(int permits) {
    if (permits < 0) throw new IllegalArgumentException();
    sync.releaseShared(permits);
}
    release方法最后会调用Semphore重写的tryReleaseShared方法

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;
    }
}

    tryReleaseShared方法执行步骤如下

    1、获取信号量个数

    2、信号量个数加1

    3、如果加1后的信号量小于原先的信号量,说明溢出了,所以抛出异常

    4、如果使用CAS操作将信号量个数更新为加1后的信号量个数,则返回true,表示归还信号量成功。

四、应用

public static void main(String[] args) {
    Semaphore mutex = new Semaphore(2);
    Thread t1 = new Thread(){
        @Override
        public void run() {
            try {
                System.out.println("我是A 我要进去");
                mutex.acquire();
                System.out.println("我是A,我进来了");
                Thread.sleep(2000);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                mutex.release();
            }
        }
    };
    t1.start();
    Thread t2 = new Thread(){
        @Override
        public void run() {
            try {
                System.out.println("我是B 我要进去");
                mutex.acquire();
                System.out.println("我是B,我进来了");
                Thread.sleep(2000);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                mutex.release();
            }
        }
    };
    t2.start();
    Thread t3 = new Thread(){
        @Override
        public void run() {
            try {
                System.out.println("我是C  我要进去");
                mutex.acquire();
                System.out.println("我是C,我进来了");
                //Thread.sleep(2000);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                mutex.release();
            }
        }
    };
    t3.start();
}
    上述代码定义了2个信号量个数的Semphore,说明同一时间最多只有两个线程能够进去执行代码。运行结果如下图所示。


    可以看到,只有A线程和B线程进入了,而线程C却被挡在门外,当A线程和B线程休眠到期后,结果如下图所示


    C进去了,并执行了代码


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值