JUC-java并发编程-并发工具类-StampedLock源码

7 篇文章 0 订阅
6 篇文章 0 订阅
本文深入解析了Java并发工具类Semaphore的工作原理,通过类图和实例代码展示了Semaphore的构造和使用。详细分析了acquire()和release()方法的调用链,解释了线程如何获取和释放许可,并通过源码解读了AQS队列中的节点管理及阻塞、唤醒机制。
摘要由CSDN通过智能技术生成

JUC

Semaphore

Sempahore原理

类图:
在这里插入图片描述

Sempahore的构造方法有两个

public Semaphore(int permits) {
    //默认非公平的
    sync = new NonfairSync(permits);
}

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

实例代码:

//创建Semaphore对象
Semaphore semaphore = new Semaphore(3);

for(int i=0;i<5;i++){
    new Thread(()->{
        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        try {
            log.info("running...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("end...");
        } finally {
            semaphore.release();
        }
    }).start();
}

源码:

1.调用Semaphore semaphore = new Semaphore(3); 实际是调用了public Semaphore(int permits) {
//默认非公平的
sync = new NonfairSync(permits);
}构造方法,在NonfairSync(int permits)中,是调用了父类Sync的Sync(int permits)构造方法,

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

最后将permits赋值给Sync的state属性,state为AbstractQueuedSynchronizer(简称AQS)的private volatile int state;

2.Sempahore.acqure()解锁过程方法调用链

  • 1.acquire() -> sync.acquireSharedInterruptibly(1)->AQS.acquireSharedInterruptibly(1);
  • 2.AQS.acquireSharedInterruptibly(1) ->NonfairSync.tryAcquireShared(int acquires)
  • 3.NonfairSync.tryAcquireShared(int acquires) -> Sync.nonfairTryAcquireShared(int acquires)
  • 4.Sync.nonfairTryAcquireShared(int acquires) 返回值大于等于0表示获取资源成功
  • 5.Sync.nonfairTryAcquireShared(int acquires) 返回值小于0表示获取资源失败,返回到AQS.acquireSharedInterruptibly(1)
  • 6.AQS.acquireSharedInterruptibly(1) ->AQS. doAcquireSharedInterruptibly(int arg)
  • 7.AQS. doAcquireSharedInterruptibly(int arg)先创建一个队列节点Node(Node的状态为Shared),将Node加入到等待队列中(此时线程还在执行,还未阻塞),获取Node节点的前置节点,判断前置节点是否是头节点,如果是头节点则再次尝试调用NonfairSync.tryAcquireShared(int acquires)获取资源,此时如果获取成功则将前置节点从队列中删除,Node作为新的前置节点并唤醒后续为SHARED节点;如果不是头节点或者尝试获取资源失败,则调用AQS.shouldParkAfterFailedAcquire()的for (;😉 循环逻辑。,第一次调用会将判断前置是否是头节点,是头节点则将头节点的状态waitStatus改为-1并且返回false,然后再经历一遍上述操作(如果是头节点的后继节点会再次调用NonfairSync.tryAcquireShared(int acquires)获取资源)的for (;😉 循环逻辑。如果还未获取到锁,则再次致谢AQS.shouldParkAfterFailedAcquire(),AQS.shouldParkAfterFailedAcquire()返回为true,进入另一个条件判断parkAndCheckInterrupt(),在parkAndCheckInterrupt()会调用LockSupport.park()将线程阻塞。
  • 总结:第一个要进入等待队列的节点线程,一共会尝试三次调用NonfairSync.tryAcquireShared(int acquires)来获取资源

源码:

public class Semaphore implements java.io.Serializable {
    public void acquire() throws InterruptedException {
        //sync没有重写AQS的acquireSharedInterruptibly
        //这里直接调用AQS的acquireSharedInterruptibly方法
        sync.acquireSharedInterruptibly(1);
    }
    
    abstract static class Sync extends AbstractQueuedSynchronizer {
    	final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
    }
    
    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);
        }
    }
}

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    
 	public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
    
    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);
                    //如果r>=0 表示获取成功,则进入成功流程
                    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);
        }
    }
    
    /**
     * 生成等待队列的节点对象
     */
    private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }
    
     private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // Record old head for check below
        setHead(node);
         
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }
    
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            return true;
        if (ws > 0) {
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
    
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }
}

1.加解锁的流程

加锁过程

Sempahore有点像停车场,permits就好像停车场的车位数量,当线程获得了permits就像是获得了停车位,然后停车场显示空余车位减一。

刚开始设置,permits(state)为3,这是有5个线程来获取资源
在这里插入图片描述

假设其中的Thread-1,Thread-2,Thread-4 cas竞争成功,而Thread-0和Thread-3竞争失败,进入AQS队列park阻塞。
在这里插入图片描述

其中Thread-1,Thread-2,Thread-4 在Sync.nonfairTryAcquireShared()方法中,调用cas都成功了,成功获取到了资源。

而Thread-0在Sync.nonfairTryAcquireShared()方法中返回失败。导致进入流程doAcquireSharedInterruptibly(arg);的for (;😉 循环逻辑。

  • 先添加节点到等待队列,判断是否前置节点为头节点(此时确实是头节点)。
  • thread-0的前置节点为头节点,所以调用NonfairSync.tryAcquireShared(int acquires)再次尝试获取资源,由于Thread-1,Thread-2,Thread-4 还未执行完所以获取失败。
  • 则进入AQS.shouldParkAfterFailedAcquire()方法,方法中判断前置节点的状态waitStatus,此时waitStatus为0,则调用compareAndSetWaitStatus(pred, ws, Node.SIGNAL);将前置节点的waitStatus改为-1并且返回false。
  • 返回false后再次执行循环,进行上述操作。
  • 由于Node(thread-0)还是头节点的后继节点,所以再次进行调用NonfairSync.tryAcquireShared(int acquires)再次尝试获取资源。由于Thread-1,Thread-2,Thread-4 还未执行完所以获取失败,所以还是失败。
  • 再次调用AQS.shouldParkAfterFailedAcquire()方法,此时头节点状态为-1,AQS.shouldParkAfterFailedAcquire()方法返回true,
  • 然后进入if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())语句的后一个条件判断,调用parkAndCheckInterrupt(),
  • 在parkAndCheckInterrupt()方法内调用LockSupport.park()阻塞thread-0线程

Thread-3同样的在Sync.nonfairTryAcquireShared()方法中返回失败。导致进入流程doAcquireSharedInterruptibly(arg);的for (;😉 循环逻辑。

  • 先添加节点到等待队列,判断是否前置节点为头节点(此时前置节点不为头节点,而是Node(thread-0))。
  • 直接进入AQS.shouldParkAfterFailedAcquire()方法,方法中判断前置节点的状态waitStatus,此时Node(thread-0))的waitStatus为0,则调用compareAndSetWaitStatus(pred, ws, Node.SIGNAL);将前置节点Node(thread-0))的waitStatus改为-1并且返回false。
  • 返回false后再次执行循环,进行上述操作。
  • 由于前置节点还是Node(thread-0)并不是头节点,所以再次调用AQS.shouldParkAfterFailedAcquire()方法,此时Node(thread-0)节点的状态waitStatus为-1,AQS.shouldParkAfterFailedAcquire()方法返回true,
  • 然后进入if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())语句的后一个条件判断,调用parkAndCheckInterrupt(),
  • 在parkAndCheckInterrupt()方法内调用LockSupport.park()阻塞thread-0线程

3.Sempahore.release()解锁过程方法调用链:

  • Semaphore.release()->Sync.releaseShared(1)->AQS.releaseShared(1)
  • AQS.releaseShared(1)->Sync.tryReleaseShared(1)
  • Sync.tryReleaseShared(1)失败则表示解锁失败
  • Sync.tryReleaseShared(1)成功则AQS.releaseShared(1)调用AQS. doReleaseShared()
  • AQS. doReleaseShared()方法中获取头节点head,判断头节点是否为空,为空则不需要唤醒;不为空则判断头节点状态是否为-1如果为-1,则调用cas将头节点状态改为0,并且唤醒头节点的后继节点。另外如果头节点状态为0则将头节点状态改为PROPAGATE
public class Semaphore implements java.io.Serializable {
    public void release() {
        sync.releaseShared(1);
    }
    
    abstract static class Sync extends AbstractQueuedSynchronizer {
    	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;
            }
        }
    }    
}


public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {

    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
    
     private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                //判断前置节点的状态是否为-1(Node.SIGNAL)
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        //尝试设置失败重新循环再次cas 直至成功
                        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;
        }
    }
}

解锁过程:

此时Thread-4执行完毕调用release()方法释放了permits,状态如下:
在这里插入图片描述

Thread-4调用了release()方法将头节点后继节点唤醒,这里唤醒Node(Thread-0)。

  • Thread-0在parkAndCheckInterrupt()方法的LockSupport.park(this);代码处被唤醒,继续执行return Thread.interrupted();

  • private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }
    
  • 返回到AQS.doAcquireSharedInterruptibly(int arg)方法的if (shouldParkAfterFailedAcquire(p, node) &&
    parkAndCheckInterrupt())逻辑判断出

  • if (shouldParkAfterFailedAcquire(p, node) &&
        parkAndCheckInterrupt())
        throw new InterruptedException();
    
  • 此时如果thread-0没有被其他线程调用interrupt()方法(如果已被调用intterupt方法则thread-0抛出异常InterruptedException),则parkAndCheckInterrupt()返回false。

  • thread-0再次进入AQS.doAcquireSharedInterruptibly(int arg)的for(;😉,调用NonfairSync.tryAcquireShared(int acquires)再次尝试获取资源,此时没有其他线程竞争资源,thread-0获取资源成功,调用int r = tryAcquireShared(arg)返回0。

  • 然后调用setHeadAndPropagate(node, r);将Node(Thread-0)设置为头节点,并调用Node的thread属性设置为null,将原来的头节点从队列中删除

  • 判断if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0) ,判断是否继续唤醒后继节点,由于h.waitStatus 为-1进入逻辑块

  • if (s == null || s.isShared())判断后继节点是否为SHREAED,此时的后继节点为Node(Thread-3)SHARED,所以调用doReleaseShared();唤醒Node(Thread-3)

  • 此时Thread-3被唤醒,但是Thread-3再调用NonfairSync.tryAcquireShared(int acquires)尝试获取资源时失败(已经没有资源),所以Thread-3会再次调用LockSupport.park()方法进入阻塞

接下来Thread-0竞争成功,permits再次设置为0,设置自己为head节点,断开原来的head节点,unpark接下来的Thread-3节点,但由于permits是0,因此Thread-3在调用NonfairSync.tryAcquireShared(int acquires)获取资源尝试不成功后再次进入park状态。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值