java并发包中Semaphore使用及源码分析

Semaphore是一个线程控制器,初始化时,给定一个线程数量,用来控制同一时刻,只能有指定数量的线程在执行,如果有其他线程,只能等待其中的线程释放掉之后,才可以继续执行。

给出一个简单的使用例子

public class SemaphoreDemo {
    public final static int SEM_SIZE = 10;

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(SEM_SIZE);
        MyThread t1 = new MyThread("t1", semaphore);
        MyThread t2 = new MyThread("t2", semaphore);
        t1.start();
        t2.start();
        int permits = 5;
        System.out.println(Thread.currentThread().getName() + " trying to acquire");
        try {
            semaphore.acquire(permits);
            System.out.println(Thread.currentThread().getName() + " acquire successfully");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release();
            System.out.println(Thread.currentThread().getName() + " release successfully");
        }


    }
}
class MyThread extends Thread {
    private Semaphore semaphore;

    public MyThread(String name, Semaphore semaphore) {
        super(name);
        this.semaphore = semaphore;
    }

    public void run() {
        int count = 3;
        System.out.println(Thread.currentThread().getName() + " trying to acquire");
        try {
            semaphore.acquire(count);
            System.out.println(Thread.currentThread().getName() + " acquire successfully");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release(count);
            System.out.println(Thread.currentThread().getName() + " release successfully");
        }
    }
}

执行结果

main trying to acquire
main acquire successfully
t2 trying to acquire
t2 acquire successfully
t1 trying to acquire
main release successfully
t1 acquire successfully
t2 release successfully
t1 release successfully

main线程先去尝试获取,传入进去的参数是5,这时候,state的值由最初设定的10,变为5,接下来,t2线程尝试去获取,将state的值降低减到2,当t1线程尝试去获取时,发现state的值,是小于acquire的值,获取失败,进行挂起,当main线程释放之后,t1线程就可以继续执行。

Semaphore的源码分析

Semaphore的内部结构是

Sync, 分别由NonfairSync 和 FairSync实现,整体来说和ReentrantLock比较类似,不同的是,sync方法当中实现的是AQS中的tryAcquireShared,对共享锁的获取方式和tryReleaseShared,

当线程中调用acquire方法的执行代码逻辑

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

在AQS类中

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

默认的非公平的情况下,会判断当前state值和acquire,返回剩余的state值

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

如果小于0,doAcquireSharedInterruptibly方法

private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED); //将当前线程添加到链表最后
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor(); //找到当前节点的前驱节点
                if (p == head) {
                    int r = tryAcquireShared(arg); // 如果是头节点,判断state的值是否大于0
                    if (r >= 0) {
                        setHeadAndPropagate(node, r); //如果state值大于0,将当前节点设为head节点
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) && //如果state的值小于0,挂起节点状态位SIGNAL的后一个节点
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

关于release方法

public void release() {
        sync.releaseShared(1);
    }
  public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
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;
    }
}

释放时,将state的值,在原来基础上加1,如果更新成功返回true,接下来执行释放操作

private void doReleaseShared() {
      
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus; //获取到头节点的状态
                if (ws == Node.SIGNAL) {  
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) //将头结点状态更新为0
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);   //唤醒头结点的下个节点
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }
 private void unparkSuccessor(Node node) {
       
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }

唤醒头结点的后一个节点

以上分析,可以看出Semaphore和CountDownLatch的实现方式是很类似的。两者都是用的是同步队列,而CyclicBarrier使用的是AQS中的条件队列。

如有问题欢迎指正~







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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值