tryAcquire的实现,从中我们可以看出在公平锁中,每一次的tryAcquire都会检查CLH队列中是否仍有前驱的元素,如果仍然有那么继续等待,通过这种方式来保证先来先服务的原则;而非公平锁,首先是检查并设置锁的状态 acquire(1),acquire(1)再调用tryAcquire()。 公平锁的tryAcquire /** * Fair version of tryAcquire. protected final boolean tryAcquire(int acquires) { //注意:这是公平的tryAcquire() final Thread current (),但是公平锁的tryAcquire()与非公平锁的tryAcquire()不同,非公平锁没有 !
handleShortCircuitViaFallback(); } } 这个方法调用了getExecutionSemaphore来获取TryableSemaphore,执行之前进行tryAcquire /** * Use like this: *
* *
* if (s.tryAcquire ();
/**
* ONLY call release if tryAcquire returned true *
*
*
* if (s.tryAcquire()) {
* try { ,release、getNumberOfPermitsUsed
TryableSemaphoreActual
/**
* Semaphore that only supports tryAcquiretryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt (); } 以acquire方法为例子、同步器中并没有实现tryAcquire的方法、 protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); } 具体的实现需要通过子类重写tryAcquire方法来实现、接下来介绍addWaiter、acquireQueued node; } } else { initializeSyncQueue(); } } } 可以看出在当前线程tryAcquire try { for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire
boolean tryAcquire() // 仅在调用时此信号量中有给定数目的许可时,才从此信号量中获取这些许可。 boolean tryAcquire(int permits) // 如果在给定的等待时间内此信号量有可用的所有许可,并且当前线程未被中断,则从此信号量获取给定数目的许可。 boolean tryAcquire(long timeout, TimeUnit unit) 对于JUC的Semaphore源码,此篇不阐述了,另开新篇;但对分布式的Semaphore倒是可以研究下 就在线程A进行release()之后,会publish,细节可查看上面的release()中的lua脚本,当B监听到事件时,就会调用Semaphore.release(),再次进行tryAcquire( 都使用Semaphore.tryAcquire()不行吗?
一、滑动时间窗口 我为RateLimiter定义了如下这个简单的IRateLimiter接口,唯一的无参方法TryAcquire利用返回的布尔值确定当前是否超出设定的速率限制。 public interface IRateLimiter { bool TryAcquire(); } public sealed class SliddingWindowRateLimiter valueTask.IsCompleted) _ = valueTask.Result; Trim(); } } } } 在实现的TryAcquire _nextWindowStartTimeTicks = DateTimeOffset.UtcNow.Add(window).Ticks; } public bool TryAcquire } return _count < _permit && Interlocked.Increment(ref _count) <= _permit; } } 在实现的TryAcquire
(int arg) { return super.tryAcquire(arg); } @Override protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); } (3)实现 tryAcquire 先看看其它锁怎么做的。 protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread @Override protected boolean tryAcquire(int acquires) { if(isHeldExclusively
() tryAcquire(long timeout, TimeUnit unit) 代码 ? ---- 常用和需要注意的方法 acquire() 获取一个许可 acquire(int permits) 获取指定个数的许可 tryAcquire()方法尝试获取1个许可证 tryAcquire(long timeout, TimeUnit unit) 最大等待许可的时间 tryAcquire(int permits) 获取指定个数的许可 tryAcquire(int permits, long timeout ---- semaphore.tryAcquire() 尝试获取一个许可,如果未获取到,不等待,将直接丢弃该线程不执行 ? 运行输出: ? ---- tryAcquire(long timeout, TimeUnit unit) 未获取到许可,设置等待时长 ? 输出结果: ? tryAcquire通过参数指定了5秒的等待时间。
Implemented * by invoking at least once {@link #tryAcquire}, * returning on success. This value is conveyed to * {@link #tryAcquire} but is otherwise uninterpreted and * tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } 如果满足的话,则会调用selfInterrupt,其中会调用Thread.currentThread().interrupt(),进行当前线程的中断操作 而在FairSync中重写了tryAcquire 方法 /** * Fair version of tryAcquire.
Implemented * by invoking at least once {@link #tryAcquire}, * returning on success. tryAcquire(arg) && // tryAcquire返回false会把当前线程放入AQS阻塞队列 acquireQueued(addWaiter(Node.EXCLUSIVE ), arg)) selfInterrupt(); } AQS并没有提供可用的tryAcquire方法,tryAcquire方法需要子类自己定制化,所以这里代码(3)会调用 ReentrantLock重写的tryAcquire方法。 公平锁的话只需要看FairSync重写的tryAcquire方法。
Implemented * by invoking at least once {@link #tryAcquire}, * returning on success. tryAcquire(arg) && // tryAcquire返回false会把当前线程放入AQS阻塞队列 acquireQueued(addWaiter(Node.EXCLUSIVE ), arg)) selfInterrupt(); } AQS并没有提供可用的tryAcquire方法,tryAcquire方法需要子类自己定制化,所以这里代码(3)会调用 ReentrantLock重写的tryAcquire方法。 公平锁的话只需要看FairSync重写的tryAcquire方法。
tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。 tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。 Implemented * by invoking at least once {@link #tryAcquire}, * returning on success. 当一个线程lock()时,会调用tryAcquire()独占该锁并将state+1。 此后,其他线程再tryAcquire()时就会失败,直到此线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。 一般来说,自定义同步器要么是独占方法,要么是共享方式,他们也只需实现tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared中的一种即可。
重点关注重写的tryAcquire与tryRelease方法,他俩分别是加锁和释放锁的重要逻辑。 tryAcquire is implemented in * subclasses, but both need nonfair try for trylock method. */ AQS的模板模式 acquire方法作为模板方法,包含了未实现方法tryAcquire,相当于抽象类。 (这里没有实现成抽象类的原因是AQS还可以提供共享锁的实现框架,而tryAcquire属于独占锁依赖的方法,实现共享锁没必要实现独占锁的抽象方法) public final void acquire(int tryAcquire如果有线程持有锁,判断是否是当前线程持有的,如果是则变成重入锁state+1,否则返回false获取失败。
(1)tryAcquire源码 public boolean tryAcquire() { //【1】 return tryAcquire(1, 0, MICROSECONDS);} public ,区别只在于acquire少了一个提前返回的操作(见tryAcquire源码的第【5】步骤) 其余的方法源码的逻辑,是跟tryAcquire一样的,此处不再赘述 总结:acquire与tryAcquire tryAcquire() 方法: 当调用 tryAcquire() 方法时,如果当前请求速率未超过限制,则请求会被立即处理,并且方法会立即返回 true。 如果当前请求速率超过了限制,tryAcquire() 方法不会阻塞当前线程,而是立即返回 false。 4、总结 总结一下,当使用客户端限流时,两种方法:acquire() 和 tryAcquire() 的主要区别在于是否阻塞当前线程。
非公平锁在 CAS 失败后,和公平锁一样都会进入到 tryAcquire 方法,在 tryAcquire 方法中,如果发现锁这个时候被释放了(state == 0),非公平锁会直接 CAS 抢锁,但是公平锁会判断等待队列是否有线程处于等待状态 setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire tryAcquire is implemented in * subclasses, but both need nonfair try for trylock method. */ final boolean false; } 公平锁 /** * 没有进行任何操作,直接获取锁 */ final void lock() { acquire(1); } /** * Fair version of tryAcquire * 这里与非公平锁只有一个区别:判断等待队列是否有线程处于等待状态 */ protected final boolean tryAcquire(int acquires) { final
* 尝试获取许可 * * @return 若获取成功则返回 true,否则返回 false */ public synchronized boolean tryAcquire * 尝试获取许可 * * @return 若获取成功则返回 true,否则返回 false */ public synchronized boolean tryAcquire 800); for (int i = 0; i < 15; i++) { boolean acquire = slidingWindowRateLimiter.tryAcquire * 尝试获取许可 * * @return 若获取成功则返回 true,否则返回 false */ public synchronized boolean tryAcquire com.artisan.LeakyBucketRateLimiter - 剩余容量:2 09:01:58.915 [main] INFO com.artisan.LeakyBucketRateLimiter - tryAcquire
tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt() ; } tryAcquire()的调用判定中是通过if(! tryAcquire())作为第1个条件的,如果返回true,则判定就不会成立了,自然后面的acquireQueued动作就不会再执行了,如果发生这样的情况是最理想的。 无论多么乐观,征用是必然存在的,如果征用存在则owner自然不会是自己,tryAcquire()方法会返回false,接着就会再调用方法:acquireQueued(addWaiter(Node.EXCLUSIVE tryAcquire(arg)这个方法我们前面介绍过,成立的条件为:锁的状态为0,且通过CAS尝试设置状态成功或线程的持有者本身是当前线程才会返回true ○ 如果这个条件成功后,发生的几个动作包含:
tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt (); } //这个方法在AbstractQueuedSynchronizer里留给子类来实现, protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); } } 这里分别会调用tryAcquire方法用于获取锁,如果失败,会调用addWaiter方法,将当前线的 在AbstractQueuedSynchronizer里tryAcquire方法是留给子类来实现的,下面来看看ReentrantLock里的非公平锁是如何实现这里的逻辑的。 (arg)) { / 如果前一个节点是头结点,则当前节点是FIFO队列里的第一个节点,则会再次调用tryAcquire方法尝试获取锁。
但AQS的tryAcquire()方法是个保护方法,需要由子类重写。 于是执行AQS的acquire()方法 -> 执行NonfairSync的tryAcquire()方法。 发现也是失败的,所以继承自Sync的NonfairSync的tryAcquire()方法返回false。 如果NonfairSync的tryAcquire()方法返回false,说明锁已被占用。 注意:NonfairSync的tryAcquire()方法会判断是否重入锁 + 是否已释放锁。
ReentrantLock调用lock方法,最终会调用sync的tryAcquire函数,获取资源。 FairSync的tryAcquire函数,**当前线程只有在队列为空或者时队首节点的时候,才能获取资源,否则会被加入到阻塞队列中。 3000897897090466540L; final void lock() { acquire(1); } protected final boolean tryAcquire = Thread.currentThread()); } 非公平锁原理 NoFairSync同样继承Sync,ReentrantLock调用lock方法,最终会调用sync的tryAcquire函数,获取资源 而NoFairSync的tryAcquire函数,会调用父类Sync的方法nofairTryAcquire函数。
和FairSync调用的acquire(int arg)方法中的tryAcquire方法,其实现是不同的。 NonfairSync调用的acquire方法,其底层tryAcquire调用的是NonfairSync重写的tryAcquire方法;FairSync调用的acquire方法,其底层tryAcquire 调用的是FairSync重写的tryAcquire方法。 tryAcquire(arg)为false,就不会再执行后面代码。反之,若! FairSync类里重实现的tryAcquire与NonfairSync最终执行的tryAcquire区别?