semphore源码分析

目录

1:semphore的使用说明

参考这篇博客:https://www.cnblogs.com/crazymakercircle/p/13907012.html

2:semphore的使用案例

package com.saytoyou.com.thread;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class Student implements Runnable {

    private String studentName;

    private Semaphore semaphore;

    /**
     * 学生构造函数
     *
     * @param studentName
     * @param semaphore
     */
    public Student(String studentName, Semaphore semaphore) {
        this.studentName = studentName;
        this.semaphore = semaphore;
    }

    @Override
    public void run() {

        try {
            semaphore.acquire();
            System.out.println("我是"+studentName+"开始排队");

            System.out.println("我是"+studentName + "开始打饭了" );
            TimeUnit.SECONDS.sleep(3);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("打饭完成" + studentName);
            semaphore.release();
        }


    }
}
package com.saytoyou.com.thread;

import java.util.concurrent.Semaphore;

public class SemaphoreTest {

    public static void main(String[] args) {

        //模拟学生食堂打饭,设置三个窗口同时执行
        Semaphore semaphore = new Semaphore(3,true);

        for (int i = 0; i < 100; i++) {
            new Thread(new Student("学生" + i, semaphore)).start();
        }

    }
}

3:semphore的源码分析

3.1:构造函数分析

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

构造semphore,permits是线程的个数,fair设置公平锁和非公平锁

3.2: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) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

分析:

1:首先将节点放入等待队列中,返回当前节点,注意第一次的头节点是new空节点,不是当前节点

2:拿到当前节点的前驱节点,判断前驱节点是否为头节点,如果为头节点则尝试去获取资源,获取成功当前state减一,返回剩余state,并尝试唤醒后续节点

   如果不是头节点,则放入FIFO阻塞队列,等待后续唤醒

3:加入state的初始值为3,那么前三个线程都可以进入获取资源,后面的就需要等待线程释放,唤醒

4:第一次node节点进入,head是新节点,指向第一个节点,第二个节点跟这进来,状态改为signal,第三个节点同上,第一个节点进入后获取资源并唤醒第二个,第二个把头介点设置为自己,接着唤醒第三个,后面stagte资源不足了,就需要不断循环尝试获取了

3.3:tryAcquireShared源码分析

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

分析:获取当前state的值,拿当前的state减去请求数,一般都是1,重新设置state的值,返回state的最新值

 

结合上面的分析为:进来之后进入阻塞队列中,判断节点的前驱节点是不是head如果是,就尝试获取资源,获取成功之后,唤醒后续节点

 

流程图如下:

 

 

4:释放资源源码分析

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

释放资源,state的值加一,这样后续第四个线程被唤醒,其实是第一个线程释放state,然后唤醒第四个线程的

 4.1 唤醒后续的等待线程

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

此时head是指向第三个线程的,状态为signal,唤醒后一个线程

 

posted @ 2022-04-14 14:21  xzlnuli  阅读(53)  评论(0编辑  收藏  举报