ReentrantLock解析及源码分析 (3)

加锁入口:lock()方法;lock()方法定义在ReentrantLock类里面,通过调用同步器的加锁操作完成加锁过程,公平机制不影响对外暴露的接口

/** * Acquires the lock. * * <p>Acquires the lock if it is not held by another thread and returns * immediately, setting the lock hold count to one. * * <p>If the current thread already holds the lock then the hold * count is incremented by one and the method returns immediately. * * <p>If the lock is held by another thread then the * current thread becomes disabled for thread scheduling * purposes and lies dormant until the lock has been acquired, * at which time the lock hold count is set to one. */ public void lock() { sync.lock(); }

官方的注释说明了lock操作会做那些事情:

获取锁,如果所没有被其他线程获取,那么将获取这个锁并返回,并设置这个锁的状态为1标识被加锁一次(锁被获取一次)

如果当前线程已经持有锁,那么锁计数器自增1

如果锁被其他线程持有,那么当前线程会被阻塞进入睡眠状态并进入同步的等待队列,直到锁被获取,然后将锁的计数器设置为1

\(\color{#00FF00}{这里面好像没说明公平的方式哈,只要锁没被获取就立刻获取,感觉公平锁是绿的}\)

加锁的过程直接调用了同步器的lock()方法在上述的同步器中lock()调用acquire()方法

acquire()方法在AQS中实现

具体解释见注释

/** * Acquires in exclusive mode, ignoring interrupts. Implemented * by invoking at least once {@link #tryAcquire}, * returning on success. Otherwise the thread is queued, possibly * repeatedly blocking and unblocking, invoking {@link * #tryAcquire} until success. This method can be used * to implement method {@link Lock#lock}. * * @param arg the acquire argument. This value is conveyed to * {@link #tryAcquire} but is otherwise uninterpreted and * can represent anything you like. */ //以独占的方法加锁,并且忽略中断 //那是不是还有响应中断的加锁呢?? public final void acquire(int arg) { //先尝试调用同步器的tryAcquire()方法加锁 if (!tryAcquire(arg) && //加锁失败的情况下将当前线程放入同步等待队列中 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //acquireQueued返回值是线程在等待期间是否被中断,如果有则还原中断现场 selfInterrupt(); }

addWaiter()方法,使用当前线程构造一个同步等待队列的节点,并且放在队尾,在AQS中实现

/** * Creates and enqueues node for current thread and given mode. * * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared * @return the new node */ private Node addWaiter(Node mode) { //为当前的线程构造一个同步等待队列中的节点,在可重入锁中式排他的模式(mode==Node.EXCLUSIVE) 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;//将当前节点放在队尾 //使用CAS操作原子的设置队尾节点 if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } //如果对尾节点为空或者CAS设置队尾节点失败调用enq方法将当前线程节点添加进队列 enq(node); return node; }

enq()方法:如果队列不存在或者使用CAS操作使节点入队失败,则进入此方法构造队列并将节点入队,在AQS中实现

/** * Inserts node into queue, initializing if necessary. See picture above. * @param node the node to insert * @return node's predecessor */ private Node enq(final Node node) { //用一个死循环将当前节点添加进队列,如果队列不存在则创建队列 for (;;) { Node t = tail; //尾节点为空则堆类不存在进行初始化队列,创建头节点 if (t == null) { // Must initialize //使用CAS操作创建头节点, //为什么式CAS操作,试想刚判断出队列不存在需要创建头节点, //此时线程发生线程的调度当前线程阻塞,另一个线程做同样的操作并创建了队列 //当前线程再次被唤醒后继续创建队列,会有线程安全问题 if (compareAndSetHead(new Node())) //尾节点指向头节点 tail = head; //队列存在死循环的将当前线程对应的节点放入到队列当中 } else { node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }

acquireQueued()方法,对于排队等待的线程应当使其进行阻塞,减少调度以及CPU空转的时间,除非下一个就到了这个线程获取锁,在AQS中实现

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyjsjx.html