java常用锁
AQS分析
我们从一个小程序入手,使用debug模式执行,代码如下:
package AboutSysn;
import java.util.concurrent.locks.ReentrantLock;
public class AQSDemo {
static ReentrantLock reeLock = new ReentrantLock();
static void m1() {
reeLock.lock();
System.out.println("AQSDemo");
reeLock.unlock();
}
public static void main(String[] args) {
m1();
}
}
可以得到下面的结果
![未命名文件 (1)](C:UserssaijiDesktop未命名文件 (1).png)
其中黄色标记的部分是AQS的重点
1.执行m1方法,调用ReentrantLock中 lock 方法,其内容如下:
为方便我们下面的分析,我们将需要扩展说的方法都起上别名,别名以 M 开头
public void lock() {
//这里的sync是我们创建的ReentrantLock对象,
sync.lock();
}
//因为我们没有使用公平锁(不交接公平锁的可以看《呀?,这就是锁(一)?》),所以
final void lock() {
//这里就是CAS算法,调用的是 AbstractQueuedSynchronizer 类里面的方法,参数的意义是将0改成1
if (compareAndSetState(0, 1)) // M1
//这里是设置将当前线程设置为独占模式
setExclusiveOwnerThread(Thread.currentThread()); //M2
else
//如果上面获取锁资源失败,进行下面的逻辑判断
acquire(1); //M3
}
2.在1 中的Lock函数中又调用了其他的函数,我们继续向深处看
//M01调用了 AbstractQueuedSynchronizer 类里的 compareAndSetState 方法,这个方法又调用了unsafe 类的compareAndSwapInt 方法,这个地方就不用再往里面看了,已经不是java语法了,那么从这里地方我们可以分析出什么呢?观察参数,
this -- 当前对象
stateoffset -- 不知道
expect -- 期望数
update -- 更新数
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
//上面的参数中只有stateoffset我们不清楚是什么,那么点进去看一下
private static final long stateOffset;
static {
try {
stateOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
} catch (Exception ex) { throw new Error(ex); }
}
// 不难发现,这个变量是从 AbstractQueuedSynchronizer 中获取的成员变量,那么我们进入到AbstractQueuedSynchronizer 看看 state 是什么?
private volatile int state;
//是一个被 volatile 修饰的 int 变量值,valatile的作用我们也很清楚,主要作用是提供了线程可见性可内存屏障,那么这个变量的作用应该就是不同线程之间通信使用,这样我们就能理解上面 compareAndSetState 函数的作用了,compareAndSetState的作用就是将 state 的值修改成自己想要的结果,同时记录修改线程(this参数的作用),如果这个操作成功了,表明锁资源已经被当前线程获取,即加锁成功
3.在2中我们分析M01的执行过程,但是W01执行有两种结果呀?M01返回值不同都做了哪些操作呢?
-
//M1返回true会执行下面这个函数 protected final void setExclusiveOwnerThread(Thread thread) { exclusiveOwnerThread = thread; } //将独占线程指向当前线程,即当前线程拥有资源
-
//M1返回false会执行 AbstractQueuedSynchronizer 中的函数,参数是1 public final void acquire(int arg) { if (!tryAcquire(arg) && //M4 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //M5 selfInterrupt(); //M5 }
4.下面是3.2中的M4方法内容
//这里时ReentrantLock中的方法
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
//得到当前线程
final Thread current = Thread.currentThread();
//获取当前state的值
int c = getState();
//如果是0,证明没有线程锁定
if (c == 0) {
//尝试加锁,调用 unsafe.compareAndSwapInt(this, stateOffset, expect, update);
if (compareAndSetState(0, acquires)) {
//如果加锁成功,设置当前线程为独占线程
setExclusiveOwnerThread(current);
return true;
}
}
//如果不是0,证明存在线程锁定当前资源,判断是不是自己锁定的?
else if (current == getExclusiveOwnerThread()) {
//如果是自己锁定的,标识冲入,对 state+1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//对state赋值
setState(nextc);
return true;
}
//没有获取到锁
return false;
}
//m4方法为返回true的话 M5方法就不会继续执行,直接结束acquire,否则执行 M5,m5实现如下
acquireQueued(addWaiter(Node.EXCLUSIVE), arg) //M5
private Node addWaiter(Node mode) {
//用当前线程和当前线程锁模式创建一个Node
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
//如果tail不为空,证明队列中已经存在等待锁的线程Node,当前节点需要加到最后
if (pred != null) {
node.prev = pred;
//同时是使用CAS
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//如果tail为空,证明没有等待队列,则新建一个
enq(node);
return node;
}
//将刚才的队列和state目标状态当做参数传入acquireQueued
final boolean acquireQueued(final Node node, int arg) {
//是否取消锁争夺,如果过程中出现异常
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//拿到前一个Thread节点
final Node p = node.predecessor();
//如果前一个是头结点,并且自己获得了锁
if (p == head && tryAcquire(arg)) {
//把自己设置成头结点
setHead(node);
//把原来head指向自己的指向取消,此时还有自己指向前一个的指向
p.next = null; // help GC
//过程没有错误
failed = false;
//返回false ,结束M3
return interrupted;
}
//如果上面的条件不成立,修改当前线程为可以parking 状态
if (shouldParkAfterFailedAcquire(p, node) &&
//park当前线程
parkAndCheckInterrupt())
//如果被park了,就需要执行M5方法,唤醒这个线程
interrupted = true;
}
} finally {
//如果过程出现异常,取消队列中的这个Thread节点
if (failed)
cancelAcquire(node);
}
}
这是第一版尝试去分析AQS源码,其中的不足支出欢迎指正,谢谢