JUC.Lock(锁机制)学习笔记[附详细源码解析]

锁机制学习笔记

目录:

  

  

  

  

  

  

  

JUC的并发包功能强大,但也不容易理解,美国空间,大神果然是用来膜拜的。经过一段时间的研究和理解,我把自己所了解的关于JUC中锁的相关知识整理下来,服务器空间,一方面给自己做个备忘,另一方面也给各位朋友做个参考。

文中源码的关键部分都做了注释,希望对大家有所帮助。另外这只是学习笔记,建议大家先去了解一些基础知识再来看其中的源码,大家有疑问的可以再参考其他文章,谢谢!

CAS的意义

CAS只是尝试性操作,可能一个线程在对比的时候,另一个线程已更改了状态,所以CAS操作可能失败。

for (;;){

do other business

}

}

CAS(obj,expect,update) 必有一个期望对象expect,一个更新对象update,expect在多线程情况下同一时间只会有一个线程能匹配,且整个CAS方法中,other business都不是共享变量,因为他们对并发无影响。

CAS经常放在循环中,在多线程情况下,就是哪个线程先匹配到expect就执行,其他线程可在下次循环中再匹配到。

锁的一些基本原理

锁其实是个独占资源,其中的state代表的就是独占资源,获取锁就是线程对state数值的增加,释放锁就是state减少的过程

1.加锁的意义在于多线程获取同一个锁,这样每个线程就会按照获取锁的顺序执行。

2.在线程内创建的对象,是每个线程独立的,因为对它的操作无需加锁,而对共享变量的操作,就必须加锁或者CAS,如果CAS失败,则代表此次操作尝试失败,需考虑后续操作

3.尽量在线程外的其他类对共享变量进行锁定(即尽量实现线程安全的类),而不要把锁带到线程内去操作锁定,因为这样会增加代码复杂性

ReentrantLock的相关代码结构

两个重要的状态I.AQS的state(int类型,32位)

用来描述有多少线程获持有锁。

在独占锁的时代这个值通常是0或者1

对于可重入锁,一个线程可多次进入,每次进入state+1

在共享锁的时代就是持有锁的数量。

tryAcquire()和tryRelease()其实就是尝试获取状态位state的修改权限并设置独占Thread

II.Node的waitStatus

对队列中节点的操作(锁定线程或释放线程)则是基于节点的waitStatus的CANCELLED = 1: 节点操作因为超时或者对应的线程被interrupt。节点不应该不留在此状态,一旦达到此状态将从CHL队列中踢出。 SIGNAL = -1:节点的继任节点是(或者将要成为)BLOCKED状态(例如通过LockSupport.park()操作),因此一个节点一旦被释放(解锁)或者取消就需要唤醒(LockSupport.unpack())它的继任节点。 CONDITION = -2:表明节点对应的线程因为不满足一个条件(Condition)而被阻塞。 正常状态 = 0:新生的非CONDITION节点都是此状态。

对于处在阻塞队列中的节点,当前节点之前的节点:waitStatus > 0的是取消的节点,在处理中应该剔除waitStatus = 0的,则需要将其改成-1

因此整个阻塞节点链的waitStatus应该为:-1,-1,-1,0

获取锁(AQS)的流程

锁的获取和释放都是基于上述2个状态来的,首先能不能获取锁是由AQS.state来控制,因此tryAcquire()和tryRelease()都是对state的控制,如果不能获取锁则需要加入到等待队列,此时线程的等待与释放则是由Node的waitStatus控制的。

下图演示了一个线程获取独占锁的过程:

I.获取锁总操作

Java Code

1234

arg){if(!tryAcquire(arg)&&acquireQueued(addWaiter(Node.EXCLUSIVE),arg))selfInterrupt();}

整个过程可分为以下四个步骤(只有tryAcquire是在Sync,其他3个都是在AQS中):

1. tryAcquire(arg):

如果tryAcquire(arg)成功,那就没有问题,已经拿到锁,整个lock()过程就结束了。如果失败进行操作2。2. addWaiter(Node.EXCLUSIVE):

创建一个独占节点(Node)并且此节点加入CHL队列末尾。进行操作3。3. acquireQueued(addWaiter(Node.EXCLUSIVE), arg):

自旋尝试获取锁,失败根据前一个节点来决定是否挂起(park()),直到成功获取到锁。进行操作4。4. selfInterrupt():

如果当前线程已经中断过,那么就中断当前线程(清除中断位)。

II.tryAcquire(尝试获取锁)

JavaCode

123456789101112131415161718

非公平锁与公平锁在tryAcquire()方法上唯一区别就是比公平锁少了 isFirst(current),它的作用就是判断AQS是否为空或者当前线程是否在队列头

III.添加到等待队列

AQS的节点结构:

上图的head,tail,prev,next这几个属性构造了一条节点链

JavaCode将节点加入到队列中

12345678910111213141516

放下一种执着,收获一种自在。放下既是一种理性抉择,也是一种豁达美。

JUC.Lock(锁机制)学习笔记[附详细源码解析]

相关文章:

你感兴趣的文章:

标签云: