ReetrantLock非公平锁底层原理

ReetrantLock非公平锁总结:

  1. 首先通过Unsafe类的CAS方法尝试将当前AQS锁的状态state从0设置为1,如果能够修改成功,那么在AbstractOwnableSynchronizer同步器中设置持有锁的线程为当前线程。

  2. 如果修改失败那么执行acquire方法
    1)通过tryAcquire方法再次尝试获取锁资源
    (1)获取AQS中的state次数,如果为0表示已经释放了锁资源,那么就可以通过自旋再次尝试获取锁资源,获取成功将同步器的持有锁的线程设置为当前线程。
    (2)判断是否为重入操作,如果是重入操作那么将AQS的State次数+1,如果+1后的值变为了负数则表示超出了可重入的最大值,那么抛出异常。如果没有问题就结束运行。
    (3)如果仍然没有获取锁资源,那么将当前线程封装成一个Node节点追加到AQS队列中。
    (4)然后在acquireQueued方法中判断是否上一个节点是头结点,如果前驱结点是头结点就再次开启自旋获取锁,获取成功了就将当前节点设为头结点,将之前的头结点的next指针设置为null帮助GC回收。如果前驱结点不是头结点并且处于唤醒状态,那么就将当前节点的线程设置为阻塞。
    (5)在设置阻塞的过程中极小的可能会发生异常,那么执行cancelAcquire方法,使该节点以及该节点之前的所有失效节点从AQS队列中清除出来。将一个有效的前驱节点指向一个有效的后继节点。

Node节点

static final class Node {  
    /** Marker to indicate a node is waiting in shared mode */  
    static final Node SHARED = new Node();  
    /** 独占锁、排它锁的标识*/  
    static final Node EXCLUSIVE = null;  
  
    /**线程是否取消的标识 */  
    static final int CANCELLED =  1;  
    /**具有这个标识标识后继节点需要被唤醒*/  
    static final int SIGNAL    = -1;  
    /**具有这个标识表示线程正在等待条件 */  
    static final int CONDITION = -2;
	//标识下一个共享请求无条件传播
    static final int PROPAGATE = -3;  
  	//记录当前节点的状态
    volatile int waitStatus;  
	volatile Node prev;  
	volatile Node next;  
  
    volatile Thread thread;  
    Node nextWaiter;  
  
    final boolean isShared() {  
        return nextWaiter == SHARED;  
    }  
  
    final Node predecessor() throws NullPointerException {  
        Node p = prev;  
        if (p == null)  
            throw new NullPointerException();  
        else            return p;  
    }  
  
    Node() {    // Used to establish initial head or SHARED marker  
    }  
  
    Node(Thread thread, Node mode) {     // Used by addWaiter  
        this.nextWaiter = mode;  
        this.thread = thread;  
    }  
  
    Node(Thread thread, int waitStatus) { // Used by Condition  
        this.waitStatus = waitStatus;  
        this.thread = thread;  
    }  
}

非公平锁

1. 非公平锁调用了NonfairSync静态内部类进行加锁

/**
 * 获取了锁资源的次数,当前线程每释放一次-1,每获取一次+1(可重入锁)
 */
private volatile int state;
//NonfairSync非公平锁
static final class NonfairSync extends Sync {  
    private static final long serialVersionUID = 7316153563782823691L;  
  	
    final void lock() {  
		//通过CAS尝试将state从0修改为1,如果为true则修改成功
        if (compareAndSetState(0, 1))  
            setExclusiveOwnerThread(Thread.currentThread());  
        else            
			acquire(1);  
    }  
    //尝试获取锁资源
    protected final boolean tryAcquire(int acquires) {  
        return nonfairTryAcquire(acquires);  
    }  
}

// AbstractQueuedSynchronizer.class
protected final boolean compareAndSetState(int expect, int update) {  
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);  
}

2. 如果CAS失败则执行acquire(int arg)

public final void acquire(int arg) {
	//tryAcquire(args)再次尝试获取锁资源,如果尝试成功,返回true
	//当尝试后仍然失败
	if (!tryAcquire(arg) &&
		//获取锁资源失败后,将当前线程封装成一个Node,追加到AQS队列中
		acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
		//线程中断
		selfInterrupt();
}
2.1 nonfairTryAcquire再次尝试获取锁资源
final boolean nonfairTryAcquire(int acquires) {  
	//获取当前线程
    final Thread current = Thread.currentThread();  
	//获取AQS的state的值
    int c = getState();
	//如果state==0 代表之前占有锁的线程已经将锁释放了,我可以再次尝试占有锁资源
    if (c == 0) {  
		//通过CAS再次尝试获取锁资源
        if (compareAndSetState(0, acquires)) { 
			//如果获取成功,设置ExclusiveOwnerThread为当前线程
            setExclusiveOwnerThread(current);  
            return true;        }  
    } 
	//如果当前线程是占有锁资源的线程  说明进行了重入锁的重入操作
    else if (current == getExclusiveOwnerThread()) {  
		//将state+1
        int nextc = c + acquires;
		//如果+1后小于0,也就是+1后超出了可重入的最大值,那么抛出异常
        if (nextc < 0) // overflow  
			//Maximum lock count exceeded  超过了最大锁数
			// 01111  + 1 = 11111 变为了负数
            throw new Error("Maximum lock count exceeded");  
		//没有问题就重新设置state值
        setState(nextc);  
        return true;    }  
    return false;  
}
2.2 addWaiter
//说明前面获取资源失败,放到队列中等待
private Node addWaiter(Node mode) {  
	//创建Node节点,并且设置thread为当前线程,设置为排它锁
    Node node = new Node(Thread.currentThread(), mode);  
    // 获取AQS队列中的尾部节点  
    Node pred = tail;  
	//如果为空则表示队列中什么都没有
	//不为空则表示队列中存在其他节点 那么做一个添加节点的操作
    if (pred != null) {  
		//将当前节点的上一个节点的指针指向队列尾部的节点
        node.prev = pred;  
		//通过CAS的方式将tail节点设置为当前节点
        if (compareAndSetTail(pred, node)) {
			//将之前尾节点的next指向当前节点
            pred.next = node;  
            return node;  
        }  
    }  
	//将该节点放到队列
    enq(node);  
	//然后返回
    return node;  
}
2.2.1 enq
//如果进入这个方法表示现在AQS队列为空,或者之前CAS的操作失败
//那么重新往队列尾部添加
private Node enq(final Node node) {  
    for (;;) {  
		//重新获取队列尾节点
        Node t = tail;  
		//如果为空则表示没人排队
        if (t == null) { 
			//初始化一个Node作为head 而这个head没有意义。
            if (compareAndSetHead(new Node()))  
				//将尾部也指向了这个没有意义的head
                tail = head;  
        } else {  
			//有人排队则执行链表的添加操作
            node.prev = t;  
            if (compareAndSetTail(t, node)) {  
                t.next = node;  
                return t;  
            }  
        }  
    }  
}

3. acquireQueued

//已经将node加入到了队列中了,才会执行这个方法
final boolean acquireQueued(final Node node, int arg) {
	//设置锁资源的标识 true为没有拿到锁资源 false为拿到了锁资源
    boolean failed = true;  
    try {  
		// 设置阻塞标识
        boolean interrupted = false;  
        for (;;) {  
			//获取当前节点的前驱结点
            final Node p = node.predecessor();
			//如果上一个节点是头结点,说明队列中我是第一个节点
			//那么我就再次尝试获取锁资源
            if (p == head && tryAcquire(arg)) { 
				//如果获取到了那么将head节点设置为当前节点
                setHead(node);  
				//将之前得到的前驱结点的next设置为null 提高GC效率
                p.next = null; // help GC  
				//设置为false表示我拿到了锁资源
                failed = false;  
				//拿到锁资源了 那么就返回false
                return interrupted;  
            }  
			//只有当上一个节点状态为-1才会将线程阻塞,等待唤醒获取锁资源
			//parkAndCheckInterrupt调用LockSupport.park()方法将线程阻塞
            if (shouldParkAfterFailedAcquire(p, node) &&  
                parkAndCheckInterrupt())  
				//将阻塞标识设置为true
                interrupted = true;  
        }  
    } finally {  
		//这里执行的几率约等于0  
        if (failed)  
            cancelAcquire(node);  
    }  
}
3.1 shouldParkAfterFailedAcquire
// pred是前驱结点  node是当前节点
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {  
	//获取上一个节点的状态
    int ws = pred.waitStatus;
	//如果上一个节点状态为SIGNAL 也就是-1,表示上一个节点是没问题的
	//那么表示当前节点需要被唤醒
    if (ws == Node.SIGNAL)  
		return true;  
	//如果上一个节点状态大于0 表示上一个节点已经失效了
    if (ws > 0) {  
		//那么将该节点指向上一个节点的指针指向上上个节点,直到指向的节点没有失效
        do {  
            node.prev = pred = pred.prev;  
        } while (pred.waitStatus > 0); 
		//将找到的前驱节点的next指针指向当前节点
        pred.next = node;  
    } else {  
        //小于等于0且不等于-1
		//将上一个有效节点状态修改为-1  因为只有是-1才会唤醒后继节点
		compareAndSetWaitStatus(pred, ws, Node.SIGNAL);  
    }  
    return false;  
}
3.2 parkAndCheckInterrupt
private final boolean parkAndCheckInterrupt() {  
    LockSupport.park(this);  
    return Thread.interrupted();  
}
3.3 cancelAcquire
private void cancelAcquire(Node node) {  
    // 健壮性判断 
    if (node == null)  
        return;  
  	//将当前node的线程设置为null,竞争锁资源和我没关系了
    node.thread = null;  
  
    //获取当前节点的前驱结点
    Node pred = node.prev;
	//如果前驱节点的状态大于0,说明这是一个失效节点
    while (pred.waitStatus > 0)  
		//找到前驱中最近的非失效节点
        node.prev = pred = pred.prev;  
  	// 尾节点执行的操作
    // 如果当前节点是最后一个节点,将尾节点设置为最近的有效节点
    if (node == tail && compareAndSetTail(node, pred)) {
		// 通过CAS方式,将尾节点的next设为null
        compareAndSetNext(pred, predNext, null);  
    } else {  
		int ws;  
		// 中间节点执行的操作
		// 如果上一个节点不是头结点
        if (pred != head &&  
			//获取上一节点状态是不是有效
			// 判断上一节点的状态是否为-1
            ((ws = pred.waitStatus) == Node.SIGNAL ||  
			 // 如果不是-1则将上一节点的状态通过CAS设置为-1
             (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && 
			//并且上一节点的线程不为空
            pred.thread != null) { 
			// 获取当前节点的后继节点
            Node next = node.next;
			// 如果后继节点不为空并且后继节点是一个有效节点
            if (next != null && next.waitStatus <= 0) 
				//通过CAS将前一个节点的next指针指向当前节点的后继节点
                compareAndSetNext(pred, predNext, next);  
        } else {
			// 头结点执行的操作
			//如果上一节点是头结点,唤醒当前节点的后继节点
            unparkSuccessor(node);  
        }  
  		//将当前节点的后继节点指向当前节点 帮助GC回收
        node.next = node;
    }  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值