提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、ReentrantLock是什么?
ReentrantLock:可重入互斥锁(独占锁)。ReentrantLock由上次成功锁定但尚未解锁的线程拥有。 当另一个线程不拥有锁时,调用lock的线程将返回并成功获取锁。 如果当前线程已经拥有锁,该方法将立即返回。
二、源码分析
源码如下:
{ ????内部类 sync = new 非公平锁同步对象 } [ private final Sync sync ] extend AbstractQueuedSynchronizer(AQS)
无参构造----->>
public ReentrantLock() {
sync = new NonfairSync();
}
有参构造
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
调用Lock方法----->>
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
cas操作----->>
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
如果当前状态值等于预期值,则原子地将同步状态设置为给定的更新值。 此操作具有volatile读写的内存语义。 参数: 期望 - 期望值 更新 - 新值 返回: 如果成功则为true 。 假返回表示实际值不等于预期值。
~>当前状态值 [volatile修饰的state] ~>state默认期望值 0
调用setExclusiveOwnerThread----->><<
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
setExclusiveOwnerThread(Thread.currentThread()); 设置当前线程拥有独占访问权限的线程。 null参数表示没有线程拥有访问权限。 此方法不会以其他方式强加任何同步或volatile字段访问。 参数: 线程 - 所有者线程
调用acquire(1)方法获取锁----->>
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
以独占模式获取,忽略中断。 通过至少调用一次tryAcquire ,成功返回。 否则线程会排队,可能会反复阻塞和解除阻塞,调用tryAcquire直到成功。 此方法可用于实现方法Lock.lock 。 参数: arg – 获取参数。 这个值被传递给tryAcquire但不会被解释并且可以代表任何你喜欢的东西。 如果返回true获取到锁, ! tryAcquire = true 将不执行 if 条件
调用非公平锁内部类方法tryAcquire----->>
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
进入nonfairTryAcquire(int acquires)方法----->>
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
执行不公平的 tryLock。 tryAcquire 在子类中实现,但两者都需要对 trylock 方法进行非公平尝试。 · 获取当前状态值state,判断c0,设置独占线程 —— 同上cas操作↑ · else if (当前线程 获取的独占线程) 说明独占线程第二次以上获取锁,设置变量nextc = c + acquires等价于 nextc = state + 1; setState操作,返回true。
判断tryAcquire尝试是否获取锁&& addWaiter方法>>>当前线程加入等待队列,让当前线程进入自旋状态,等待线程
进入acquireQueued方法
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null;
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
以独占不间断模式获取已在队列中的线程。 由条件等待方法以及获取使用。 参数: 节点——节点 arg – 获取参数 返回: 如果在等待时中断,则为true
总结
提示:这里对文章进行总结: ??/????公平锁: /??????非公平锁: \ ??????重入:同一个线程可以多次获取锁 ??\ ????不可重入:同一个线程不能多次获取锁,需要和其他线程一样去等待队列中的线程释放之后才能获取锁。
CAS原理: ReentrantLock乐观锁
ReentrantLock和Synchronized的区别?
什么是乐观锁与悲观锁? 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样当第二个线程想拿这个数据的时候,第二个线程会一直堵塞,直到第一个释放锁,他拿到锁后才可以访问。传统的数据库里面就用到了这种锁机制,例如:行锁,表锁,读锁,写锁,都是在操作前先上锁。java中的synchronized的实现也是一种悲观锁。
乐观锁:乐观锁概念为,每次拿数据的时候都认为别的线程不会修改这个数据,所以不会上锁,但是在更新的时候会判断一下在此期间别的线程有没有修改过数据,乐观锁适用于读操作多的场景,这样可以提高程序的吞吐量。在Java中java.util.concurrent.atomic包下面的原子变量就是使用了乐观锁的一种实现方式CAS实现。
|