引自图灵,用于学习
ReentrantLock lock = new ReentrantLock(false);//false为非公平锁,true为公平锁
3个线程
T0 T1 T2
lock.lock() //加锁
while(true){
if(cas加锁成功){//cas->比较与交换compare and swap,
break;跳出循环
}
//Thread.yeild()//让出CPU使用权
//Thread.sleep(1);
HashSet,LikedQueued(),
HashSet.add(Thread)
LikedQueued.put(Thread)
阻塞。
LockSupport.park();
}
T0获取锁
xxxx业务逻辑
xxxxx业务逻辑
lock.unlock() //解锁
Thread t= HashSet.get()
Thread t = LikedQueued.take();
LockSupport.unpark(t);
Lock,公平与公平两种特性
三大核心原理
自旋,LocksSuport, CAS,queue队列
CAS依赖汇编指令:cmpxchg()
Lock可重入性:可重入!
synchronized:可重入
公平
exclusiveOwnerThread 当前获取锁的线程是谁!
state 状态器
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire(arg) //锁竞争逻辑
//CLH
addWaiter(Node.EXCLUSIVE) //线程入队,Node节点,Node对Thread引用
Node:共享属性,独占属性 //响应式编程,异步非阻塞,FutureTask,Callbale
创建节点Node = pre,next,waitestate,thread 重要属性
waitestate节点的生命状态:信号量
SIGNAL = -1 //可被唤醒
CANCELLED = 1 //代表出现异常,中断引起的,需要废弃结束
CONDITION = -2 // 条件等待
PROPAGATE = -3 // 传播
0 - 初始状态Init状态
为了保证所有阻塞线程对象能够被唤醒
compareAndSetTail(t, node) 入队也存在竞争
//当前节点,线程要开始阻塞
acquireQueued(Node(currentThread), arg)
节点阻塞之前还得再尝试一次获取锁:
1,能够获取到,节点出队,并且把head往后挪一个节点,新的头结点就是当前节点
2、不能获取到,阻塞等待被唤醒
1.首先第1轮循环、修改head的状态,修改成sinal=-1标记处可以被唤醒.
2.第2轮循环,阻塞线程,并且需要判断线程是否是有中断信号唤醒的!
shouldParkAfterFailedAcquire(p, node)
waitestate = 0 - > -1 head节点为什么改到-1,因为持有锁的线程T0在释放锁的时候,得判断head节点的waitestate是否!=0,如果!=0成立,会再把waitstate = -1->0,要想唤醒排队的第一个线程T1,T1被唤醒再接着走循环,去抢锁,可能会再失败(在非公平锁场景下),此时可能有线程T3持有了锁!T1可能再次被阻塞,head的节点状态需要再一次经历两轮循环:waitState = 0 -> -1
Park阻塞线程唤醒有两种方式:
1、中断
2、release()