AQS
AQS(AbstractQueuedSynchronizer)是 RetrentLock 与 Java 并发包工具的实现根底,其底层采纳乐观锁,大量应用了 CAS 操作,并且在抵触时,采纳自旋形式重试,以实现轻量级锁和高效的获取锁
CAS 的全称为 Compare-And-Swap,是一条 CPU 的原子指令,其作用是让 CPU 比拟后原子地更新某个地位的值,通过考察发现,其实现形式是基于硬件平台的汇编指令,就是说 CAS 是靠硬件实现的,JVM 只是封装了汇编调用
其实现次要由状态、队列、CAS 三局部组成:
状态:在 AQS 中,状态由 state 属性来示意,该属性的值示意了锁的状态,state 为 0 示意锁没有被占用,state 大于 0 示意已被占用
队列:在 AQS 中,队列的实现是一个双向链表,它示意所有期待锁的线程的汇合,当线程获取锁失败时通过 CAS 操作将本人退出队列的开端
CAS 操作: 操作系统层面提供的 API,CAS 是一条 CPU 原语,其蕴含三个操作数——内存地位、预期原值和新值,如果内存地位的值与预期原值相匹配,那么处理器会主动将该地位值更新为新值
执行流程
- 线程尝试获取锁,将 state 的状态通过 CAS 操作由 0 改写成 1
- 未获取到锁,则将本人封装成 node,通过 CAS 操作退出队列尾部
- 如果前置节点是头节点,则会再次尝试获取锁
- 将前驱节点状态设置为 signal 后,通过 LockSupport.park,挂起以后线程
- 拿到锁的线程执行完逻辑后,通过 LockSupport.unPark 唤起后置节点线程,后置节点线程则会再次尝试去获取锁
AQS 超时机制
通过 LockSupport 的 parkNanos 实现
并发容器
ConcurentHashMap 实现
jdk1.7
- 应用一个 Segment 数组和多个 HashEntry 组成,每个数组桶位上面挂一个 HashEntry
- 当执行 put 操作时,会进行第一次 key 的 hash 来定位 Segment 的地位,如果该 Segment 还没有初始化,即通过 CAS 操作进行赋值,而后进行第二次 hash 操作,找到相应的 HashEntry 的地位,再通过 ReentrantLock 进行加锁后,将数据增加 HashEntry 中的对应地位
jdk1.8
- 与 HashMap 构造统一,底层是数组 + 链表或数组加红黑树结构实现
- 作流程是,对 key 进行 hash 运算定位到数组下标,如果下标地位链表为空则先初始化,再 cas 插入,如果有数据,则用同步锁 Synchronized 对数组桶位进行加锁后插入
这个改变阐明了 jdk 团队认为通过优化后的 Synchronized 比 ReentrantLock 效率高
ThreadLocal
ThreadLocal 是一个线程隔离变量
ThreadLocal.set 办法相当于给以后线程的 TheadLocalMap 属性赋值,key 就是 TheadLocal 本人,value 是要 set 的值
应用场景:
- 多线程应用一些线程不平安的工具类时,ThreadLocal 复用工具对象
- 用来传递上下文,能够防止对象的跨层传递。(定义一个工具类,类中定义一个 TheadLocal 动态属性,通过工具类调用 TheadLocal 的 set 和 get 办法)
- 具体场景:之前 oracle 迁徙 mysql 数据库时,切换开关应用 ThreadLocal 保留至以后线程,防止开关值的上下文传递
内存泄露场景:
因为 TheadLocalMap 与线程生命周期统一,在应用线程池等线程生命周期长的场景应用 TheadLocal 时,如不手动执行 remove 操作,value 便不会被回收,造成内存泄露