获取锁
public boolean tryLock() {
//调用内部类的获取锁方法
return sync.nonfairTryAcquire(1);
}
官方文档
Acquires the lock only if it is not held by another thread at the time of invocation.
仅当在调用时未由另一个线程持有锁时,才获取锁。
Acquires the lock if it is not held by another thread and returns immediately with the value true
, setting the lock hold count to one. Even when this lock has been set to use a fair ordering policy, a call to tryLock()
will immediately acquire the lock if it is available, whether or not other threads are currently waiting for the lock. This "barging" behavior can be useful in certain circumstances, even though it breaks fairness. If you want to honor the fairness setting for this lock, then use tryLock(0, TimeUnit.SECONDS)
which is almost equivalent (it also detects interruption).
如果锁未被另一个线程持有,则获取锁,并立即返回值 true
,将锁保持计数设置为 1。即使此锁已设置为使用公平排序策略,调用 也会 tryLock()
立即获取锁(如果可用),无论其他线程当前是否正在等待锁。这种“吠叫”行为在某些情况下可能很有用,即使它破坏了公平性。如果要遵守此锁的公平性设置,请使用几乎等效 tryLock(0, TimeUnit.SECONDS)
的设置(它还会检测中断)。
If the current thread already holds this lock then the hold count is incremented by one and the method returns true
.
如果当前线程已经持有此锁,则保留计数将递增 1,该方法返回 true
。
If the lock is held by another thread then this method will return immediately with the value false
.
如果锁由另一个线程持有,则此方法将立即返回值 false
.
-
Specified by: 指定者:
tryLock
in interfaceLock
tryLock
在接口Lock
中 -
Returns: 返回:
true
if the lock was free and was acquired by the current thread, or the lock was already held by the current thread; andfalse
otherwise
true
如果锁是空闲的并且被当前线程获取,或者锁已经被当前线程持有;以及其他false
Sync#nonfairTryAcquire
调用内部类的获取锁方法
java.util.concurrent.locks.ReentrantLock.Sync#nonfairTryAcquire
/**
* Base of synchronization control for this lock. Subclassed
* into fair and nonfair versions below. Uses AQS state to
* represent the number of holds on the lock.
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//基于cas获取锁
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
底层是基于cas
AbstractQueuedSynchronizer#compareAndSetState
底层是基于cas来获取锁
java.util.concurrent.locks.AbstractQueuedSynchronizer#compareAndSetState
/**
* Atomically sets synchronization state to the given updated
* value if the current state value equals the expected value.
* This operation has memory semantics of a {@code volatile} read
* and write.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that the actual
* value was not equal to the expected value.
*/
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update); //cas,原子操作:底层都是unsafe的native方法
}
其实就是AQS把cas封装了一下而已
释放锁
/**
* Attempts to release this lock.
*
* <p>If the current thread is the holder of this lock then the hold
* count is decremented. If the hold count is now zero then the lock
* is released. If the current thread is not the holder of this
* lock then {@link IllegalMonitorStateException} is thrown.
*
* @throws IllegalMonitorStateException if the current thread does not
* hold this lock
*/
public void unlock() {
//释放锁
sync.release(1);
}
调用AQS的释放锁方法
AbstractQueuedSynchronizer#release
java.util.concurrent.locks.AbstractQueuedSynchronizer#release
/**
* Releases in exclusive mode. Implemented by unblocking one or
* more threads if {@link #tryRelease} returns true.
* This method can be used to implement method {@link Lock#unlock}.
*
* @param arg the release argument. This value is conveyed to
* {@link #tryRelease} but is otherwise uninterpreted and
* can represent anything you like.
* @return the value returned from {@link #tryRelease}
*/
public final boolean release(int arg) {
//释放锁
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//唤醒其他阻塞的线程
unparkSuccessor(h);
return true;
}
return false;
}
Sync#tryRelease
java.util.concurrent.locks.ReentrantLock.Sync#tryRelease
/**
* 释放锁
*
* @author gzh
* @date 2022/1/28 7:50 PM
* @param releases
* @return boolean
*/
protected final boolean tryRelease(int releases) { //入参值是1
//释放锁:就是把state值减1为0
int c = getState() - releases; //1-1 = 0 //当前值是1,减去入参值1,就是0
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) { //如果等于0,就是释放锁成功
free = true;
setExclusiveOwnerThread(null); //
}
setState(c); //更新值为0
return free;
}
释放锁:就是把state值减1为0。
总结
1、显式锁ReentrantLock,基于AQS。
2、AQS基于cas。
3、cas不是锁。其实就是无锁。
那为什么无锁可以实现锁的功能?
java cas到底是有锁还是无锁?
CAS(Compare And Swap)是一种无锁(lock-free)操作,它是一种并发编程中的技术,用于在多线程环境下实现同步而不需要使用传统的锁机制。CAS操作是原子性的,它会比较内存中的值与预期值,如果相等,则将新值写入内存,否则操作失败,但不会阻塞线程。
无锁编程的目标是减少锁竞争带来的性能开销,从而提高多线程程序的性能和并发性。CAS是一种典型的无锁操作,它允许多个线程在没有明确锁定的情况下更新共享变量。
说白了,就是:
1、可以并发写(基于cas)
但是只有一个写能成功,其他写都失败,不阻塞。而传统写,没有成功的写都会阻塞。ReentrantLock的获取锁方法和并发包集合类的写方法,一般都是基于cas——注意,不是直接使用cas,而是基于AQS,AQS只是封装了cas而已,说白了,就是比直接使用cas更方便一点而已。
2、可以并发读(基于volatile)
不管是否有并发写或者并发读,都不影响当前线程读,从而大大提高读性能。而传统锁,读也会阻塞,哪怕两个线程只是并发读也会互相阻塞。并发包集合类的读方法,一般都是基于volatile,所以既不会阻塞,并且可以读到最新数据。比如ConcurrentHashMap的读方法,就是基于volatile,具体细节参考:juejin.cn/post/719595…
以上也是并发包的意义所在,并发包解决线程安全的核心就是基于无锁(cas + valotile)
。而无锁和传统锁的区别,关键在于是否阻塞
。无锁的写,要么成功,要么直接失败(不会阻塞)。无锁的读,100%可以成功,而且可以读到最新数据,完全不受其他线程写和读的任何影响,更不会阻塞。
官方文档-ReentrantLock
public class ReentrantLock
extends Object
implements Lock, Serializable
A reentrant mutual exclusion Lock
with the same basic behavior and semantics as the implicit monitor lock accessed using synchronized
methods and statements, but with extended capabilities.
一种可重入互斥, Lock
其基本行为和语义与使用方法和语句访问 synchronized
的隐式监视器锁相同,但具有扩展功能。
A ReentrantLock
is owned by the thread last successfully locking, but not yet unlocking it. A thread invoking lock
will return, successfully acquiring the lock, when the lock is not owned by another thread. The method will return immediately if the current thread already owns the lock. This can be checked using methods isHeldByCurrentThread()
, and getHoldCount()
.
A ReentrantLock
由线程拥有,上次成功锁定,但尚未解锁。当锁不属于另一个线程时,调用的线程 lock
将返回,成功获取锁。如果当前线程已拥有锁,该方法将立即返回。这可以使用方法 isHeldByCurrentThread()
和 getHoldCount()
进行检查。
The constructor for this class accepts an optional fairness parameter. When set true
, under contention, locks favor granting access to the longest-waiting thread. Otherwise this lock does not guarantee any particular access order. Programs using fair locks accessed by many threads may display lower overall throughput (i.e., are slower; often much slower) than those using the default setting, but have smaller variances in times to obtain locks and guarantee lack of starvation. Note however, that fairness of locks does not guarantee fairness of thread scheduling. Thus, one of many threads using a fair lock may obtain it multiple times in succession while other active threads are not progressing and not currently holding the lock. Also note that the untimed tryLock()
method does not honor the fairness setting. It will succeed if the lock is available even if other threads are waiting.
此类的构造函数接受可选的公平性参数。在争用下设置 true
时,锁有利于授予对等待时间最长的线程的访问权限。否则,此锁不保证任何特定的访问顺序。使用由许多线程访问的公平锁的程序可能显示比使用默认设置的程序更低的总吞吐量(即,速度较慢;通常慢得多),但在获取锁和保证没有饥饿的时间上差异较小。但请注意,锁的公平性并不能保证线程调度的公平性。因此,使用公平锁的众多线程中的一个可能会连续多次获取它,而其他活动线程没有进展并且当前没有持有锁。另请注意,不计时 tryLock()
方法不支持公平性设置。如果锁可用,即使其他线程正在等待,它也会成功。
It is recommended practice to always immediately follow a call to lock
with a try
block, most typically in a before/after construction such as:
建议的做法是始终立即使用 try
块调用 , lock
最典型的是在构造之前/之后,例如:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
In addition to implementing the Lock
interface, this class defines a number of public
and protected
methods for inspecting the state of the lock. Some of these methods are only useful for instrumentation and monitoring.
除了实现接口之外, Lock
此类还定义了许多用于检查锁状态的 public
and protected
方法。其中一些方法仅对检测和监视有用。
Serialization of this class behaves in the same way as built-in locks: a deserialized lock is in the unlocked state, regardless of its state when serialized.
此类的序列化的行为方式与内置锁相同:反序列化的锁处于解锁状态,而不考虑其序列化时的状态。
This lock supports a maximum of 2147483647 recursive locks by the same thread. Attempts to exceed this limit result in Error
throws from locking methods.
此锁最多支持同一线程的 2147483647 个递归锁。尝试超过此限制 Error
会导致锁定方法引发。