Semaphore使用与源码进阶

 
一、使用场景
   主要是用来控制同时执行线程的数量,用以保护临界资源
 
 
二、使用实例
package com.test.lock;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;


public class SemphoreTest4 {

    static Logger logger = LoggerFactory.getLogger(SemphoreTest4.class ) ;

    public static void main(String[] args) throws InterruptedException {
        Semaphore semphore = new Semaphore(2);
        for (int i = 1; i <=29 ; i++) {
            new Thread(()->{
                try {
                    logger.info(String.format("t %s准备完成", Thread.currentThread().getName() ));
                    semphore.acquire();
                    logger.info(String.format(" 线程 %s 正在执行。。。。。", Thread.currentThread().getName()));
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                semphore.release();
                logger.info(String.format("t %s 执行结束", Thread.currentThread().getName()));
            },"t"+(i+20)).start(); ;
        }

    }
}
三、源码主要方法解读
1)构造方法
public Semaphore(int permits) {
    //设置AQS中status的值
    sync = new NonfairSync(permits);
}

2)semphore.acquire();

public void acquire() throws InterruptedException {
    //当前线程获取执行令牌
    sync.acquireSharedInterruptibly(1);
}

3) 可以执行还是需要去排队

public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0) //获取执行权限
        doAcquireSharedInterruptibly(arg); //排队
}

 4)尝试获取执行权限

protected int tryAcquireShared(int acquires) {
    for (;;) {
        //如果队列里面已经有排队的,则直接返回-1 去排队
        if (hasQueuedPredecessors())
            return -1;
        //获取当前的状态
        int available = getState();
        //计算剩余的许可数量
        int remaining = available - acquires;
        if (remaining < 0 || //如果<0 则返回去排队
            compareAndSetState(available, remaining)) //如果获取许可 成功,则返回去执行
            return remaining;
    }
}
5)线程执行完成,则释放许可
 
//循环+CAS数据最终的一致性
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;
    }
}

 6) 释放许可以后需要唤醒下面线程

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

 四、整体思路是:

   1 设置标识位
   2 线程过来获取许可,如果可以获取到,则继续执行
   3 如果获取失败,则进入队列等待,直到被唤醒
   4 释放许可,并唤醒后面线程
原文地址:https://www.cnblogs.com/lean-blog/p/13736073.html