【ReentrantLock源码分析】1.xdb中的使用 2.获取和阻塞(阻塞前的一些死心不改)的源码
1.xdb中的使用例子
在xdb中,我们大概执行业务时的流程简化如下:
package org.example.testReentrantLock;import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;public class Main {public static final ReentrantLock lock = new ReentrantLock();public static void main(String[] args) throws InterruptedException {new Thread(() -> {try {lock.lock();// 这里相当于执行业务逻辑,在事务内再次持有一次锁,加入到自己本地的LockList中lock.lock();System.out.println("1111");TimeUnit.SECONDS.sleep(2);lock.unlock();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}, "a").start();TimeUnit.SECONDS.sleep(1);new Thread(() -> {try {lock.lock();System.out.println("2222");} finally {lock.unlock();}}, "b").start();}
}/*
1111
2222*/
思考:为啥我们用ReentrankLock而不是synchronized呢?因为:我们需要提供可重入的特性,而且释放是要控制在:事务完成后(不管是回滚了还是提交成功了)才释放锁,因此要使用这种显式锁。
2.源码分析
step1
public static final ReentrantLock lock = new ReentrantLock();
实现如下:
public ReentrantLock() {sync = new NonfairSync();}
可以看出来,默认情况下,是非公平锁,想想目前我还没有遇到要用公平锁的情况。
step2
接下来是上锁:
lock.lock();
/*** 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.如果这个锁没有被其它线程持有,那么会立即获取锁,并且设置锁的持有次数为1** <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.如果此锁被其它线程持有了,那么此线程立即变成不可用然后进入睡眠状态,直到锁被获取到那时,锁的持有数量被设置为1*/public void lock() {sync.acquire(1);}
进入到acquire里面:// && 符号的使用,前面为true才会走后门
public final void acquire(int arg) {if (!tryAcquire(arg) &&// 这个很重要,如果没有获取到,也就是返回了false,那么肯定要检查阻塞acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}
这里走非公平下的获取:
static final class NonfairSync extends Sync {private static final long serialVersionUID = 7316153563782823691L;protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}}
再次进入看下非公平模式下如何获取锁的:
abstract static class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = -5179523762034025860L;/*** Performs non-fair tryLock. tryAcquire is implemented in* subclasses, but both need nonfair try for trylock method.*/@ReservedStackAccessfinal boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();// 查看下当前的状态,这个state是加了volatile的,volatile int state;int c = getState();// 根据此状态位判断出来,如果没有被其它线程持有if (c == 0) {// 使用cas,如果当前是0,则设置为1if (compareAndSetState(0, acquires)) {// 设置当前线程占有了setExclusiveOwnerThread(current);return true;}}// 如果不是0,而且就是这个线程持有的else if (current == getExclusiveOwnerThread()) { // 计数+1int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");// 设置下最新的计数setState(nextc);return true;}// 走到这,说明其它线程已经持有了(state既不是0,也不是自己占有)return false;}
既然没有获取到,那么肯定就要阻塞自己了
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
如何实现阻塞的呢?进去 // 可以发现aqs在没有获取到的时候,其实是先cas死心不改尝试获取下
/** Various flavors of acquire, varying in exclusive/shared and* control modes. Each is mostly the same, but annoyingly* different. Only a little bit of factoring is possible due to* interactions of exception mechanics (including ensuring that we* cancel if tryAcquire throws exception) and other control, at* least not without hurting performance too much.*//*** Acquires in exclusive uninterruptible mode for thread already in* queue. Used by condition wait methods as well as acquire.** @param node the node* @param arg the acquire argument* @return {@code true} if interrupted while waiting*/final boolean acquireQueued(final Node node, int arg) {boolean interrupted = false;try {for (;;) {// 可以看出来这里是aqs相对于synchronized的优化,没获取到暂时不死心// 这里依然再走一下尝试获取final Node p = node.predecessor();if (p == head && tryAcquire(arg)) {// 走这里,说明还真获取到了setHead(node);p.next = null; // help GCreturn interrupted;}// 走到这说明真是获取不到了,那么应该是进入阻塞状态了if (shouldParkAfterFailedAcquire(p, node))// 这里可以看出来,是调用的park实现的阻塞interrupted |= parkAndCheckInterrupt();}} catch (Throwable t) {cancelAcquire(node);if (interrupted)selfInterrupt();throw t;}}
真的没办法了,阻塞自己呗
private final boolean parkAndCheckInterrupt() {LockSupport.park(this);return Thread.interrupted();}
park
public static void park(Object blocker) {// 要被阻塞的线程Thread t = Thread.currentThread();setBlocker(t, blocker);// 这里是真的阻塞了,是一个native方法U.park(false, 0L);setBlocker(t, null);}
【ReentrantLock源码分析】1.xdb中的使用 2.获取和阻塞(阻塞前的一些死心不改)的源码相关推荐
- 「源码分析」CopyOnWriteArrayList 中的隐藏知识,你Get了吗?
前言 本觉 CopyOnWriteArrayList 过于简单,寻思看名字就能知道内部的实现逻辑,所以没有写这篇文章的想法,最近又仔细看了下 CopyOnWriteArrayList 的源码实现,大体 ...
- lodash源码分析之compact中的遍历
小时候, 乡愁是一枚小小的邮票, 我在这头, 母亲在那头. 长大后,乡愁是一张窄窄的船票, 我在这头, 新娘在那头. 后来啊, 乡愁是一方矮矮的坟墓, 我在外头, 母亲在里头. 而现在, 乡愁是一湾浅 ...
- apollo源码分析 感知_Kitty中的动态线程池支持Nacos,Apollo多配置中心了
目录 回顾昨日 nacos 集成 Spring Cloud Alibaba 方式 Nacos Spring Boot 方式 Apollo 集成 自研配置中心对接 无配置中心对接 实现源码分析 兼容 A ...
- lodash源码分析之baseFindIndex中的运算符优先级
我悟出权力本来就是不讲理的--蟑螂就是海米:也悟出要造反,内心必须强大到足以承受任何后果才行. --北岛<城门开> 本文为读 lodash 源码的第十篇,后续文章会更新到这个仓库中,欢迎 ...
- 【FFMPEG源码分析】ffmpeg中context与AVClass,AVOption之间的关系
通过前面三篇文章的分析大致了解了ffmpeg中demuxer/decoder模块的内部大致结构和数据处理流程.在阅读源码的过程中经常会看到XXXContext,AVClass xxx_class, A ...
- [pig4cloud框架源码分析] 03 - MyBatis中的sql语句日志打印
文章目录 导读 pig4cloud框架配置 Mybatis Log Plugin 插件开启方式 插件说明 [TODO]源码分析 拦截器方案实现sql日志查看 参考资料 导读 使用MyBatis开发过程 ...
- UGUI源码分析:LayoutGroup中的纵横布局组件(HorizontalOrVerticalLayoutGroup)
系列 UGUI源码分析系列总览 相关前置: UGUI CanvasUpdateSystem源码分析 UGUI源码分析:LayoutSystem布局系统 文章目录 系列 UML图一览 LayoutGro ...
- Linux内核源码分析—Linux内核中的嵌入式汇编
转载请注明出处: http://blog.csdn.net/weifenghai/article/details/52794872 概述: 内核中分配文件描述符时找第一个0的位置的一个底层函数,了 ...
- java 最少使用(lru)置换算法_「Redis源码分析」Redis中的LRU算法实现
如果对我的文章感兴趣.希望阅读完可以得到你的一个[三连],这将是对我最大的鼓励和支持. LRU是什么 LRU(least recently used)是一种缓存置换算法.即在缓存有限的情况下,如果有新 ...
最新文章
- python免费入门手册-python基础入门手册。。。。。。
- IT知识架构和操作系统简介1
- 你不得不掌握的thinkphp5
- html文本最小长度,CSS中处理不同长度文本的几种小技巧
- LeetCode-116. 填充每个节点的下一个右侧节点指针
- CSS3/jQuery创意盒子动画菜单
- matlab 查找脉冲产生时间,脉冲时间信号MATLAB表示
- 免费 | 开源操作系统年度盛会最新日程曝光,邀您一同开启烧脑模式!
- mysql 优化器提示_Mysql查询优化器
- 通信原理-数字基带传输
- python控制苹果手机触摸屏失灵怎么办_iPhone6触屏失灵,用一会就失灵,很恼火?...
- xpub ypub zpub ---- btc address
- 电脑win7系统开机密码忘记
- 【读书笔记】关键影响力:如何调动团队力量
- 【华为云会议开发指南】开发流程
- 关于insight数据库价格与价值的双重选择
- html盒子里的内容溢出,[经验] HTML页面中子盒子溢出了怎么办
- 王子与公主的爱情故事新结局
- vim的替换字符串的命令
- 高一计算机组装与维护教学总结,计算机组装与维护教学总结
热门文章
- CF Edu 47E. InterCity Travelling 期望线性,打表
- 详尽可能性模型(转载)
- Libra-Platform微服务平台之全链路蓝绿灰度发布
- 奋斗吧,程序员——第三十三章 今朝此为别,何处还相遇
- 程序员辞职信走红职场!干活累还是读书累?答:不快乐才是最累的
- 《SolidWorks 2012中文版从入门到精通》一6.2 查询
- .NET基础笔记(C#)
- SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord)
- r3 4300u和i3 10110u有什么区别
- B2C免运费1年记:京东拖垮小企业后再收费