渣某人读源码之ReentrantLock之FairSync之加锁

瞎扯

好家伙,CSDN这写一个文章好像很复杂的样子啊!!!!

被AQS的源码折磨的死去活来的

好家伙,最近看AQS的源码的时候,从什么acquire开始就啥也没看懂,拖了两天,实在受不了了,我就去B站找了个视频,嘿嘿,真棒,老师讲的真好。

看一下Idea的神仙断点技能,渣某人比较菜,最近才发现

我在这里加了断点,方便对源码的debug,然后断点有条件的来着
![

上Demo

package thread;

import java.sql.Time;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author :zhangst.
 * @Date :Created in 22:55 2021/1/26
 * @Version: 1.0
 */
class Phone {
    Lock lock = new ReentrantLock(true);


    public void sleepLongTime() {
        lock.lock();
        try {
            TimeUnit.MINUTES.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
public class ReenterLockDemo {
    public static void main(String[] args) {
        Phone phone = new Phone();
        //最开始只有一个A线程(是指后面的都注释了),Debug条件是"A".equals(Thread.currentThread().getName())
        new Thread(() -> {
            try {
                phone.sleepLongTime();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "A" ).start();
        //然后加上了B线程,Debug条件是"B".equals(Thread.currentThread().getName())
        new Thread(() -> {
            try {
                phone.sleepLongTime();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "B" ).start();
        //最后加上了C线程,Debug条件是"C".equals(Thread.currentThread().getName())
        new Thread(() -> {
            try {
                phone.sleepLongTime();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "C" ).start();
    }
}

然后是我对一点源码的注解,写的不好,勿喷啊,哈哈

ReentrantLock

public void lock() {
	sync.lock();
}

final void lock() {
	acquire(1);//T1就一直返回
}


public final void acquire(int arg) {
	if (!tryAcquire(arg) &&//T1返回True,false直接返回,T2返回false
		acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//T2 初始化节点之后,直接返回T2的Node,
		selfInterrupt();
}

//尝试获取锁
protected final boolean tryAcquire(int acquires) {
	final Thread current = Thread.currentThread();//当前线程
	int c = getState();//T1 取到0,T2 state为1
	if (c == 0) {
		if (!hasQueuedPredecessors() &&//T1 !hasQueuedPredecessors 为true,继续执行,设置 acquires 为 1
			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;
}

public final boolean hasQueuedPredecessors() {
	// The correctness of this depends on head being initialized
	// before tail and on head.next being accurate if the current
	// thread is first in queue.
	Node t = tail; // Read fields in reverse initialization order
	Node h = head;
	Node s;
	return h != t &&
		((s = h.next) == null || s.thread != Thread.currentThread());//T1 进来,头等于尾,h != t直接返回false
}

private Node addWaiter(Node mode) {
	Node node = new Node(Thread.currentThread(), mode);
	// Try the fast path of enq; backup to full enq on failure
	Node pred = tail;
	if (pred != null) {
		node.prev = pred;//T3走到这里,入队列
		if (compareAndSetTail(pred, node)) {
			pred.next = node;
			return node;
		}
	}
	enq(node);//T2直接走到这里
	return node;
}

public static void park(Object blocker) {
	Thread t = Thread.currentThread();
	setBlocker(t, blocker);
	UNSAFE.park(false, 0L);//确切的说是停在这里,别问,问就是debug出来的
	setBlocker(t, null);
}

private Node enq(final Node node) {
	for (;;) {
		Node t = tail;
		if (t == null) { // Must initialize
			if (compareAndSetHead(new Node()))//T2第一次循环,初始化节点
				tail = head;
		} else {//T2再一次进来的时候,tail已经不是空了
			node.prev = t;//设置这个,后面有用
			if (compareAndSetTail(t, node)) {//入队列
				t.next = node;
				return t;
			}
		}
	}
}

private final boolean compareAndSetHead(Node update) {
	return unsafe.compareAndSwapObject(this, headOffset, null, update);
}


final boolean acquireQueued(final Node node, int arg) {//T2进入arg = 1
	boolean failed = true;
	try {
		boolean interrupted = false;
		for (;;) {//死循环,编译成字节码时,短而且快
			final Node p = node.predecessor();//返回node 的前节点,T3的前节点就是T2嘛
			if (p == head && tryAcquire(arg)) {//T2p == head 为 true,执行tryAcquire,返回false;T3的前节点不是头,过
				setHead(node);
				p.next = null; // help GC
				failed = false;
				return interrupted;
			}
			if (shouldParkAfterFailedAcquire(p, node) &&//T2先返回false,然后重新死循环,第二次返回true,继续;T3走到这里,然后返回false,继续死循环;再一次进去,返回true;然后和T2一样卡死
				parkAndCheckInterrupt())//
				interrupted = true;
		}
	} finally {
		if (failed)
			cancelAcquire(node);
	}
}

final Node predecessor() throws NullPointerException {
	Node p = prev;
	if (p == null)//之前有设置过,为tail
		throw new NullPointerException();
	else
		return p;//T2进入这里
}

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {//注意这里的pred是前节点
	int ws = pred.waitStatus;//T2进来 waitStatus 为 0
	if (ws == Node.SIGNAL)//SIGNAL = -1,T2第二次进来,执行这里面的代码;注意T3进来的时候这里的是前节点的ws哦,是0哦,
		/*
		 * This node has already set status asking a release
		 * to signal it, so it can safely park.
		 */
		return true;
	if (ws > 0) {
		/*
		 * Predecessor was cancelled. Skip over predecessors and
		 * indicate retry.
		 */
		do {
			node.prev = pred = pred.prev;
		} while (pred.waitStatus > 0); 
		pred.next = node;
	} else {
		/*
		 * waitStatus must be 0 or PROPAGATE.  Indicate that we
		 * need a signal, but don't park yet.  Caller will need to
		 * retry to make sure it cannot acquire before parking.
		 */
		compareAndSetWaitStatus(pred, ws, Node.SIGNAL);//设置前节点 -1 为什么是设置前节点呢?只要是 -1表示睡眠转态,是否睡眠需要别;T3也是到这里;把前面的节点改成-1;然后回去死循环;为什么要自旋两次?1. 尽最大努力加锁;两次的话对于计算机性能最好
	}
	return false;
}

private final boolean parkAndCheckInterrupt() {
	LockSupport.park(this);//进入之后停在这里等待唤醒
	return Thread.interrupted();
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值