Java并发工具包一---Semaphore

1、什么是Semaphore?

Semaphore 字面意思是信号量的意思,它的作用是控制访问特定资源的线程数目(就是对一个资源的访问并发量最多有多高),底层依赖AQS的状态State,是在生产当中比较常用的一个工具类。

2、Semaphore的方法

2.1、构造方法

public Semaphore(int permits)

public Semaphore(int permits, boolean fair)

permits:permits参数代表同一时间访问资源的线程数量,也就是state。
fair:fair参数代表公平性,如果设置为true的话,下次执行的线程将会是等待最久的线程。

2.2、重要方法

public void acquire() throws InterruptedException 

public void release() 

tryAcquire(long timeout, TimeUnit unit)

acquire():表示获取资源,如果它有permits参数的话,代表一个线程获取一次资源时需要给state减去几,就是2.1中的state。
release():表示释放资源,如果它有permits参数的话,代表一个线程获取一次资源时需要给state加上几,就是2.1中的state。
tryAcquire:尝试获取,可以设置超时时间。举例如下:

semaphore.tryAcquire(500,TimeUnit.MILLISECONDS)

3、Semaphore简单应用

public class SemaphoreRunner {

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(2);
        for (int i=0;i<10;i++){
            new Thread(new Task(semaphore,"du+"+i)).start();
        }
    }

    static class Task extends Thread{
        Semaphore semaphore;

        public Task(Semaphore semaphore,String tname){
            super(tname);
            this.semaphore = semaphore;
        }

        public void run() {
            try {
            	//获取公共资源      
                semaphore.acquire();
				System.out.println(Thread.currentThread().getName()+":aquire() at time:"+System.currentTimeMillis());
                Thread.sleep(1000);
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

当我们执行这段代码时就会发现,每一次在访问资源时由于semaphore做了控制所以同一时间最多只有两个线程获取到资源。

4、源码简析

4.1、acquire() 方法

先看个大体流程图,如下所示:

在这里插入图片描述

进入到源码之后,我们会走到这个方法:

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) {
            	// 尝试获取共享资源,走4.1.1
                int r = tryAcquireShared(arg);
                // 如果r >= 0,说明获取成功
                if (r >= 0) {
                	// 唤醒头节点并把节点往后挪一个
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            // shouldParkAfterFailedAcquire判断节点是否损坏,如果是,则剔除
            // parkAndCheckInterrupt 阻塞节点
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
    	// 如果出现坏的节点,将会在这里被剔除
        if (failed)
            cancelAcquire(node);
    }
}

4.1.1、tryAcquireShared

protected int tryAcquireShared(int acquires) {
	// 死循环加上cas保证如果有资源使得线程一定要获取到资源再退出
    for (;;) {
    	// 如果在队列中,直接返回-1
        if (hasQueuedPredecessors())
            return -1;
        // 获取资源当前的状态值
        int available = getState();
        // 减去当前线程需要的资源值
        int remaining = available - acquires;
        // 要么不成功,将负数返回回去
        // 要么多次遍历成功
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}

4.2、release()

先看个大体流程图,如下所示:

在这里插入图片描述

我们点击release方法之后,会进入到下面这个方法

public final boolean releaseShared(int arg) {
	// 先还回信号量
	// tryReleaseShared,走4.2.1
    if (tryReleaseShared(arg)) {
    	// 判断节点是否可以唤醒获取资源并将队列往后挪,并且释放资源
    	// doReleaseShared,走4.2.2
        doReleaseShared();
        return true;
    }
    return false;
}

4.2.1、tryReleaseShared()

还回信号量

protected final boolean tryReleaseShared(int releases) {
    for (;;) {
   		// 获取当前资源的state值
        int current = getState();
        // 将即将要释放的资源值加上去
        int next = current + releases;
        if (next < current) // overflow
            throw new Error("Maximum permit count exceeded");
        // 将加了之后的值通过cas算法保证原子性将值替换过去
        if (compareAndSetState(current, next))
            return true;
    }
}

4.2.2、doReleaseShared()

判断后续节点是否可以唤醒获取资源并将队列往后挪,并且释放当前节点资源

private void doReleaseShared() {
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            // 如果是释放信号量走到这里的话,前面获取信号量时,node的state值应该是SIGNAL,说明可以唤醒
            if (ws == Node.SIGNAL) {
            	// 这块强行将waitState值转换为0,因为马上要释放锁了
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值