reentrantlock和synchornized
从前,sync加锁需要需要使用c/c++去调用本地的方法,使cpu在用户态和内核态之间转换,导致效率并不高。 jdk1.7时对其进行了优化,使其效率和reentrantlock差不多。
reentrantlock重入锁,有许多api可以调用,可直接在jdk层面解决同步问题。其分为公平锁和非公平锁,其内部有一个int类型的state用来记录是否有线程调用过.lock()方法,有一个等待队列来存储没抢到锁的线程。线程交替执行时,等待队列不被初始化,发生冲突时才会初始化该队列。
此队列是一个双向链表,队列被使用后,其头节点会一直是当前拿锁的线程的节点,由于是初始化队列,所以需要new一个thread为null的节点来代替获得锁的线程的节点作为头节点。
构造方法:
有两种构造方法,默认情况下会返回一个非公平锁,在有boolean类型参数时,true返回公平锁,false返回非公平锁。公平锁和非公平锁的区别在于是先调用CAS获取锁还是先调用acquire。
非公平锁:
会使用CAS直接尝试改变锁的状态,改变成功会返回true,这就导致如果上一个带锁线程改变状态的时间点和新来的线程尝试获取的时间刚好匹配,就会让新线程无视排队直接获取到锁。
公平锁:
acquire:
第一步尝试获取:
如果状态不为0,就判断请求线程是否是获取了锁的线程,true就计数+1,不是就跳出else if 返回 false
如果状态为0(自由)调用
查看当前的队列是否为空 与 当前头节点的next是否为空 或 头结点的next的thread是否是当前线程。(短路,s被赋值为h.next,若h.next != null ,才进行接下来的判断)
1.队列为空,返回false,可以获取锁。
2.头尾节点不同,队列不为空,头节点的下一个节点为空,表示上一个线程节点刚被弹出队列,当前线程即将成为队列里的第一个线程,返回true不可获取锁。
3.头尾节点不同,队列不为空,头的下一个节点不为空,当前请求线程不是头节点的下一个,返回true,表示没轮到这个线程拿锁,是下一个,返回false表示可以拿锁。
返回了false,则调用CAS拿锁并整个tryAcquire返回true,一路return到线程中继续执行。
返回了true,整个tryAcquire会返回false,并将当前线程加入队列。
//========================
加入队列代码如下:
总之,就是新建一个thread为null的节点,作为队列的头尾节点,并且一直保持thread==null的节点为头节点,有新线程进来时,将其尾插在当前tail节点后,并使其为尾节点。
//=======================
continue…