Java并发包:Semaphore类

Semaphore信号量

Semaphore 是一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。
Semaphore 是java.util.concurrent包下面的一个类,主要用于阻塞线程,合理调度线程的效果。

先上demo代码:

package com.java.concurrent.semaphore;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

public class SemaphoreDemo   {
    private final Semaphore semaphore;
    private boolean[] resourceArray;
    private final ReentrantLock lock;
    private final int size = 1;

    public SemaphoreDemo() {
        semaphore = new Semaphore(size,true);
        resourceArray = new boolean[size];
        lock = new ReentrantLock(true);
    }

    public void use(){
        try {
            semaphore.acquire();
            int index = get();
            if(index!=-1){
                System.out.println("index : "+index+",in...");
                Thread.sleep(1000);
                resourceArray[index]=false;
                System.out.println("index : "+index+",out.");
            }else{
                System.out.println("index : "+index);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            semaphore.release();
        }
    }

    public int get(){
        int index = -1;
        lock.lock();
        try{
            for(int i=0;i<size;i++){
                if(!resourceArray[i]){
                    resourceArray[i] = true;
                    index = i;
                    break;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
        return index;
    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        SemaphoreDemo semaphoreDemo = new SemaphoreDemo();
        for(int i=0;i<3;i++){
            executorService.submit(()->{
                semaphoreDemo.use();
            });
        }
    }
}

执行结果:
index : 0,in…
index : 0,out.
index : 0,in…
index : 0,out.
index : 0,in…
index : 0,out.

显然,可以看到在结果中在线程数大于size的时候,并不会只执行一次。由于 semaphore.acquire(); 这个代码的存在,会将其余线程阻塞起来,并公平的调用。接下来看看Semaphore的源码。

package java.util.concurrent;
public class Semaphore implements java.io.Serializable {
	//省略···
	public void acquire() throws InterruptedException {
		sync.acquireSharedInterruptibly(1);
    }
    //省略···
}

通过acquire()作为入口,可以看到内部调用了sync.acquireSharedInterruptibly(1);

//sync实现了AbstractQueuedSynchronizer这个抽象类,aqs就是一个抽象队列同步器
abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 1192457210091910933L;

        Sync(int permits) {
            setState(permits);
        }

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

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

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

        final void reducePermits(int reductions) {
            for (;;) {
                int current = getState();
                int next = current - reductions;
                if (next > current) // underflow
                    throw new Error("Permit count underflow");
                if (compareAndSetState(current, next))
                    return;
            }
        }

        final int drainPermits() {
            for (;;) {
                int current = getState();
                if (current == 0 || compareAndSetState(current, 0))
                    return current;
            }
        }
    }


//并且实现了公平锁和非公平锁
/**
     * NonFair version
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -2694183684443567898L;

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

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

    /**
     * Fair version
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = 2014338818796000944L;

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

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

由刚才的sync.acquireSharedInterruptibly(1);进入

public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
         //如果线程被打断,则抛出异常
        if (Thread.interrupted())
            throw new InterruptedException();
        //执行至少一次tryAcquireShared()
        if (tryAcquireShared(arg) < 0)
        //如果这里返回小于0,就会放到aqs的队列中排队,重复获取许可,否则一直循环直到被打断
            doAcquireSharedInterruptibly(arg);
    }

由于我们创建的时候用的是公平锁,所以看一下公平锁的实现

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

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

        protected int tryAcquireShared(int acquires) {
            for (;;) {
            	//是否为公平锁这是这里的细节不同,这里的方法是查看前面是否还有线程在排队
                if (hasQueuedPredecessors())
                    return -1;
                int available = getState();
                int remaining = available - acquires;
                //如果许可大于等于0则执行后面compareAndSetState方法,
                //cas原子操作,可以模仿一下||写法,简单又暴力
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
    }

回看上面挂起到aqs的方法

	/**
     * Acquires in shared interruptible mode.
     * @param arg the acquire argument
     */
    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        //放到等待队列里面
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
        	//无限循环,等同于while
            for (;;) {
            //获取前一个结点
                final Node p = node.predecessor();
                if (p == head) {
                	//重复获取排队信息
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                    //获取到许可的话,去掉引用方便gc,return出去
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                //除非线程被取消或者打断,否则一直循环
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

到这里基本结束,另外可以思考的点,java还有什么通讯方式,通讯方式是怎么样的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值