Semaphore源码阅读acquire与release

文章中的内容不保证完全正确,如果有误可以给我留言

Semphore的使用示例

基于AQS的共享模式实现

import java.util.concurrent.Semaphore;

public class SemaphoreSample {
    public static void main(String[] args) {
        Semaphore semaphore=new Semaphore(2);
        for(int i=0;i<5;i++){
            new Thread(new Task(semaphore,"Thread"+i)).start();
        }
    }
    static class Task extends Thread{
        Semaphore semaphore;
        public Task(Semaphore semaphore,String tName){
            this.semaphore=semaphore;
            this.setName(tName);
        }
        public void run(){
            try{
                semaphore.acquire();
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+":acquire() at time:"+System.currentTimeMillis());
                Thread.sleep(1000);
                semaphore.release();
                System.out.println(Thread.currentThread().getName()+":release() at time:"+System.currentTimeMillis());
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Semaphore的实现原理

Semaphore基于共享锁实现,在构造时,会输入一个参数permits,表示最大的共享数量。可以这样比喻,把Semaphore比作电影院的门禁,初始时设置有最大容量permits,进去一个人,permits就减一(减去的数也可以大于1),当permits为0时,再想进去只能在外面排队。当里面的人出来后,permits会加上这个人进去时减的数,这是permits不为0了,然后会通知排队的人进去,直到permits再次为0.

Semaphore的构造函数

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

acquire()

	/**
     * 获得一个许可,如果一个许可被提供那么立即返回并将可提供的许可数量减一
     * 如果没有许可可以提供,出于线程调度的目的,当前线程被停用并处于休眠状
     * 态直到下面两种情况发生:
     * 其他线程为此信号量调用release方法,当前线程将被下一个分配许可
     * 其他一些线程{@linkplain Thread#interrupt interrupts}当前线程
     */
    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
acquireSharedInterruptibly()
/**
     * 以共享模式获取,如果中断则终止,通过首先检查中断状态然后至少调用一次
     * tryAcquireShared方法,成功就返回。否则线程将排队,并可能反复阻塞和
     * 解除阻塞,并调用tryAcquireShared,直到成功或线程被中断为止
     * @throws InterruptedException if the current thread is interrupted
     */
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
tryAcquireShared()

在AbstractQueuedSynchronizer类中实现了默认处理方法

	//返回值:剩下的许可的数量
	protected int tryAcquireShared(int arg) {
	        throw new UnsupportedOperationException();
	}

在Semaphore的内部类NonfairSync和FairSync中均有实现,因为上面示例中的是使用的非公平锁,在这里展示NonfairSync中的实现

	protected int tryAcquireShared(int acquires) {
	  return nonfairTryAcquireShared(acquires);
	}
nonfairTryAcquireShared()

在Semaphore的内部类Sync中实现

	final int nonfairTryAcquireShared(int acquires) {
	  for (;;) {//在高并发场景下,compareAndSetState()方法可能会失败,所以进行自旋不停的尝试
	        int available = getState();
	         int remaining = available - acquires;
	         if (remaining < 0 ||
	             compareAndSetState(available, remaining))//使用CAS替换掉原来state的值
	             return remaining;
	     }
	 }
doAcquireSharedInterruptibly()
   /**
     * Acquires in shared interruptible mode.
     * @param arg the acquire argument
     */
    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);//将当前线程加入队列
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);//再次尝试获取资源
                    if (r >= 0) {//获取成功
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&//检查获取失败后是否因该阻塞
                    parkAndCheckInterrupt())//阻塞当前线程,暂停循环,直到被唤醒
                    throw new InterruptedException();
            }
        } catch (Throwable t) {
            cancelAcquire(node);
            throw t;
        }
    }

release()

不带参数时默认释放一个许可,带参数时可以一次释放多个许可

public void release() {
        sync.releaseShared(1);
    }
public void release(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.releaseShared(permits);
    }

releaseShared()
    public final boolean releaseShared(int arg) {
    	//先尝试释放许可,释放成功后唤醒阻塞的线程重新争夺许可
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
tryReleaseShared()

在AbstractQueuedSynchronizer中实现了默认版本

	protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
    }

在Semaphore的内部类Sync(继承自AbstractQueuedSynchronizer)的版本

		//释放许可,即设置state=state+releases,设置需为原子操作,可能会有多个线程同时释放许可
		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;
            }
        }
doReleaseShared()
 	/**
     * 把当前结点设置为SIGNAL或者PROPAGATE
     * 唤醒head.next(B节点),B节点唤醒后可以竞争锁,成功后head->B,然后又会唤醒B.next,一直重复直到共享节点都唤醒
     * head节点状态为SIGNAL,重置head.waitStatus->0,唤醒head节点线程,唤醒后线程去竞争共享锁
     * head节点状态为0,将head.waitStatus->Node.PROPAGATE传播状态,表示需要将状态向后继节点传播
     */
		private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                //需要控制并发,因为入口有setHeadAndPropagate跟release两个,避免两次unpark
                    if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                 /** head状态为SIGNAL,且成功设置为0之后,唤醒head.next节点线程
                   * 此时head、head.next的线程都唤醒了,head.next会去竞争锁,成功后head会指向获取锁的节点,
                   * 也就是head发生了变化。看最底下一行代码可知,head发生变化后会重新循环,继续唤醒head的下一个节点
                   */
                    unparkSuccessor(h);//唤醒线程后在doAcquireSharedInterruptibly方法中使用parkAndCheckInterrupt())方法阻塞的线程会重新执行doAcquireSharedInterruptibly中的循环
                }
                else if (ws == 0 &&
                         !h.compareAndSetWaitStatus(0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }
unparkSuccessor()

从队列中唤醒线程

private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
        if (ws < 0)
            node.compareAndSetWaitStatus(ws, 0);

       //若后继结点为空,或状态为CANCEL(已失效),则从后尾部往前遍历找到
       //最前的一个处于正常阻塞状态的结点进行唤醒
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node p = tail; p != node && p != null; p = p.prev)
                if (p.waitStatus <= 0)
                    s = p;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值