致敬大师,致敬未来的你
生命不止,学习不息,运动不息
Life does not stop, learning does not stop, movement does not stop
ReentranLock使用
ReentranLock使用很简单,就是把需要上锁的代码,用lock与unlock包裹就行,就像下边的代码
import java.util.concurrent.locks.ReentrantLock;
public class Test {
//默认不公平锁
static ReentrantLock lock = new ReentrantLock(true);
static void insert(){
try{
lock.lock();
//模拟业务代码执行
System.out.println("我被锁了" + Thread.currentThread().getId());
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
//模拟多线程执行
new Thread(()->{
insert();
}).start();
new Thread(()->{
insert();
}).start();
new Thread(()->{
insert();
}).start();
}
}
认识公平锁和非公平锁、自旋
ReentranLock在使用上分为公平锁和非公平锁
公平锁
卖饭窗口就是锁,人相当于一个个线程,每次接待一个人,后边每个人都在排队,这就是公平锁
非公平锁
非公平锁就相当于一群人在抢这个卖饭窗口,谁抢到谁就能买饭
自旋
自旋就相当于一个死循环,在每次循环的时候,就判断一下条件,是否要跳出这个死循环
int a = 0;
while(true){
if(a%2 == 0){
//当满足次条件时,跳出循环,否则继续循环
break;
}
if(a%3 == 0){
//当满足次条件时,跳出循环,否则继续循环
break;
}
a++;
}
ReentranLock详解
ReentranLock构造方法
构造方法分为两种,一种是无参构造方法,一种是有参构造方法
static ReentrantLock lock1 = new ReentrantLock(); //无参构造方法
static ReentrantLock lock = new ReentrantLock(true); //有参构造方法
无参构造方法
无参构造方法默认使用非公平锁
源码片段:
public ReentrantLock() {
sync = new NonfairSync();
}
有参构造方法
有参构造方法根据用户去指定使用公平锁还是非公平
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
构造方法总结
ReentranLock实例化对象时,实际使用了它的内部类
而点开Sync,可以看到这个类继承了AbstractQueuedSynchronizer,也就是我们平时所说的AQS
而Sync又有两个子类NonfairSync 、FairSync ,如下代码段所示
abstract static class Sync extends AbstractQueuedSynchronizer
static final class NonfairSync extends Sync
static final class FairSync extends Sync
公平锁详解
以下基于jdk1.8
公平锁流程
大家都知道公平锁到底是怎么回事了,那么公平锁的简单流程呢,
retrantLock源码各种定义
最小单位-node节点
最小节点全类名为java.util.concurrent.locks.AbstractQueuedSynchronizer.Node
其中主要属性解释如下:
Node SHARED : shared模式
Node EXCLUSIVE : 独占模式
Node nextWaiter: 队列模式,或者说节点类型,值为上边两个的值,SHARED 或者 EXCLUSIVE
Thread thread : 存储当前节点对应的线程
int waitStatus : 同步状态、锁类型、节点类型,存储的是下一个节点的,所以说,空队列添加第一个节点时,会构建一个空的node先添加进去
~~~~
所以这就是为什么Node用双向链表,它把当前节点的状态放到了前一个节点中
state类型如下所示
/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;
Node prev : 上一个节点
Node next : 下一个节点
retrantLock中,waitStatus的值为SIGNAL,nextWaiter为EXCLUSIVE
等待队列
在AQS也就是java.util.concurrent.locks.AbstractQueuedSynchronizer类中,用了两个属性维持着这个队列
在初始化retrantLock时,不会对次队列进行初始化,当插入第一个节点时,才会初始化
//头节点
private transient volatile Node head;
//尾节点
private transient volatile Node tail;
加锁图解
添加第一个节点
添加第二个节点
以下为整个retrantLock代码流程
解锁流程
使用park.unpark()唤醒nexNode
如果nextNode为空,则从尾节点开始遍历,找到不为空的节点(不太清楚为什么要这么做)