Semaphore

JUC工具类

Semaphore

用于控制同时访问某个资源的线程数量,默认非公平
可以用于限制对共享资源的并发访问量,以控制系统的流量。

@Slf4j
public class SemaphoreDemo {
    private static Semaphore semaphore = new Semaphore(2);

    private static Executor executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            executor.execute(() -> { getProductInfo();});
        }
    }


    private static String getProductInfo(){
        try {
            // 获取许可证
            semaphore.acquire();
            log.info("业务代码进行操作");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }finally {
            // 释放许可证
            log.info("业务代码执行结束,释放许可证");
            semaphore.release();
        }
        return "执行业务代码结束";
    }

    private static String getProductInfo2(){
        try {
            if (!semaphore.tryAcquire()){
                log.error("加载失败,请稍后重试");
                return "加载失败,请稍后重试";
            }
            log.info(Thread.currentThread() + " 正常执行业务代码");
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            // 释放许可证
            semaphore.release();
        }
        return "执行代码结束";
    }
}

执行结果
Semaphore_第1张图片

原理

在实例一个Semaphore的时候,默认是非公平的,且传参一个许可证数量,即对AbstractQueuedSynchronizer.state参数设置一个初始值
Semaphore_第2张图片
其中继承关系如下
Semaphore_第3张图片
其中acquire方法的底层实现
Semaphore_第4张图片
公平锁中
Semaphore_第5张图片
相较于非公平锁,多了一个是否存在队列判断。
判断remaining许可证是否还有,如果remaining > 0此种情况代表还有可用许可证,并尝试CAS执行,执行成功返回remaining。

如果tryAcquireShared(arg) >= 0表示当前线程已经获取到许可证,此时结束执行。
否则执行doAcquireSharedInterruptibly(arg)方法

Semaphore_第6张图片
其中addWaiter()方法如下

	// 这里的mode参数上游传来的参数Node.SHARED,是个共享的node
    private Node addWaiter(Node mode) {
    // 根据当前线程实例一个Node
        Node node = new Node(Thread.currentThread(), mode);
        Node pred = tail;
        // 如果pred不为null,先将node的上节点设置,然后执行CAS操作将当前节点设置为尾节点并将上一节点的下节点设置为当前node
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        // 如果不存在队列,此时执行enq方法
        enq(node);
        return node;
    }

enq方法,创建一个阻塞队列,并设置一个非空头结点,将当前node依次往后放入队列中

    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
            // 同样,先关联上一节点,之后执行CAS操作成功之后再将上一节点的next设置为当前节点
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

shouldParkAfterFailedAcquire()方法,对节点的waitStatus状态进行设置值

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        // 判断pred节点状态是否为-1
        if (ws == Node.SIGNAL)
            return true;
        // 如果pred节点状态大于0,则一直找直至找到上一节点的waitStatus <= 0的,并将此节点的下节点设置为node
        if (ws > 0) {
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
        	// 执行CAS操作将pred节点设置为-1
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

其中的waitStatus可以在这里查看AQS原理解析

parkAndCheckInterrupt()方法将当前线程设置为阻塞状态,并判断当前线程是否中断

    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

释放许可

    private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                // 是否需要唤醒节点
                if (ws == Node.SIGNAL) {
                	// 恢复节点初始值0
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            
                    // 唤醒.next节点
                    unparkSuccessor(h);
                }
                else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            // 只有当head为null,或者head == tail的时候,才会跳出循环。即阻塞队列中没有阻塞节点
            if (h == head)                   // loop if head changed
                break;
        }
    }

其中Semaphore是在初始化时设置State为2,在获取到许可证时state减1,释放许可证时加1,最终保证state为初始化时的值。

你可能感兴趣的:(java,jvm,servlet)