1、第一天
ReentrantLock AQS 程序 = 思想 + 代码 思想 = 解决问题的思路 银行办业务 1、看办理的人多不多,不多就直接去柜台,多就排队 2、排队的时候有等待去 3、如果行长小舅子来办业务,肯定不需要排队,直接去办理业务。 非公平 4、过号了,重新排 if (failed) cancelAcquire(node); 5、简答的运算 A&&B A||B 6、六个操作 抢锁 1、看锁标志位 state = 0 默认没有线程占有锁 state = 1 有线程占有锁 2、state = 0 没有线程占有锁,加锁需要线程修改锁标志位,需要一种同一时刻只有一个线程可以修改成功锁标志位的机制=CAS 3、state >= 1有线程栈有了锁,当前来抢锁的线程是不是占有锁的线程。 是,重入 state+1 不是,抢锁失败 4、优化:看等待区有没有人,如果有人,锁肯定被占用了。 如果判断有人在等待 hasQueuedPredecessors 公平锁、非公平锁存在差异的地方。 公平锁和非公平锁存在的唯一的差别就是一个临界区, 之前占有锁的线程已经执行外任务,释放了锁,这时候恰好来了一个线程(非公平锁)。 在现实中有存在的意义。 公平锁 if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } 非公平锁 if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } Node节点 CLH算法,单向队列 AQS 双向队列 释放锁 1、重入问题 2、锁状态位恢复为0 3、唤醒队列中等待的线程 入队 如果没有抢到锁,并且不是可重入的化,那么就需要入队了。 !tryAcquire(arg) 加锁失败 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)需要入队列,把现在转化成Node,然后设置闹钟进行阻塞。 public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } 设置闹钟 waitStatus AQS队列 队列中的第一个节点,占有锁的线程对应的节点。 入队列,队列中有没有节点, 没有的化,AQS队列会生成两个节点 有的化,AQS尾部插入。 出队列 出队 出队的事情交给被唤醒的线程来做。 阻塞 入队列完成以后,做什么 队列中的第二个节点再次抢锁,因为第一个节点是占有锁的节点。在阻塞之前尝试看看。 上闹钟 阻塞 自旋两次 前驱节点的waitStatus修改为-1,然后park了。 唤醒
2、第二天
未完待续。。。。