目录
3,synchronized和ReentrantLock有什么不同?
day4
一,复习:
1,锁的四种状态及升级过程
无锁 -> 偏向锁(只有一个线程) -> 轻量级锁(自旋锁) -> 重量级锁(系统锁)
2,volatile 可见性和禁止指令重排序怎么实现的
可见性:缓存一致新协议
禁止指令重排:JMM 模型里有8个指令完成数据读写,通过其中load和store指令相互组合成4个内存屏障实现禁止指令重排序
3,synchronized和ReentrantLock有什么不同?
synchronized | ReentrantLock | |
加解锁 | 系统自带,系统自动加解锁 | 手动加解锁 |
等待队列 | 不支持 | 支持 |
底层实现 | 了四种锁状态的升级 | CAS+AQS队列来实现 |
CAS:Compare and Swap,比较并交换。CAS有3个操作数:内存值V、预期值A、要修改的新值B。
AQS使用一个FIFO的队列表示排队等待锁的线程,队列头节点称作“哨兵节点”或者“哑节点”,它不与任何线程关联。
synchronized
AtomicXXX
自旋实现的
4,各种JUC同步锁
LockSupport | 灵活的线程工具 | |
ReentrantLock | 锁 | |
CountDownLatch | 倒数的门栓:等待结束 | |
CylicBarrier | 循环栅栏 | |
Phaser | 分阶段执行,需要多个参与的。(遗传算法) | |
ReadWriteLock - StampedLock | 读 多,写少,保证数据的一致性。读写都需要加锁,避免脏读。 | |
Semaphore | 限流,同时只允许多少个线程执行。高速口,5个收费窗口 | |
Exchanger | 两个线程交换数据使用,阻塞交换。 |
二,TestLockSupport
支持实现锁
LockSupport.park();//停车
LockSupport.unpark(t);//t线程启动运行
使用wait,notitfy指定线程,比较费劲,LockSupport就想对随意的多。
unpark可以先于park启动,效果是 前者优先于后者。但如果再次调用park 会继续停止
package com.example.demo.thread.t4juc;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
public class TestLockSupport {
public static void main(String[] args){
Thread t = new Thread(()->{
for (int i = 0; i < 10; i++) {
System.out.println(i);
if (i==5)
LockSupport.park();
if (i==8)
LockSupport.park();
TimeUnitSleep(1);
}
});
t.start();
// 停止后 休眠8秒后重启
// TimeUnitSleep(8);
// System.out.println("after 9 SECONDS");
LockSupport.unpark(t);
}
public static void TimeUnitSleep(long timeout){
try {
TimeUnit.SECONDS.sleep(timeout);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
三,面试题
实现一个容器,提供两个方法 add size
写2个线程,线程1添加10个元素到容器中,线程2实现监控元素的的个数,个数到5个时,线程2给出提示并结束;
写一个固定容量同步容器,拥有put和get方法吗,以及getCount方法,能够支持两个生产线成以及10个消费线程阻塞调用
四,源码阅读技巧
承认读源码很难
理解别人的思路
要有一定的基础:数据结构的基础 和 设计模式(坦克第一版 23种设计模式)
读思路骨架,不读细节
五,阅读原则
跑不起来不读 :读起来费劲,多态造成找不到具体的子类实现,进行调试debug就会直至子类实现
解决问题就好 - 目的性 : 想象祖传代码
一条线索到底 : 具体实现会有方法套方法,不要整体读,要一条跟进下去,找到具体线索后,在往下读。顺着线读而不是面
无关细节略过 : 边界值 n-1 n+1 之类的 ,只读对当前有用的
一般不读静态
一般动态读法
day5
一,AQS源码解析
1,画图帮助自己理解记忆
(1)方法之间的调用图(每个方法做了什么事情)
(2)类的继承关系图(理清多态关系)
2,AQS核心是
volatile int state值(用来控制首次抢【使用CAS的逾期值确定当前线程是独占】和可重入【如果当前线程是独占,则+1,改变头,表示可重入】)和一个双向链表(存的线程)
CAS(compare and swap的缩写,比较并交换。)的方式入队出队
用CAS代替锁整个链表,从而大大提高了效率
Template Method 模板方法 :父类的方法 由子类实现
Callback Function
父类默认实现
子类具体实现
二,VarHandle
1.9之后出现,指得是指向某个变量的引用,通过这个引用可以找到这个变量,可以做原子性的操作(CAS)。C和C++的实现,使用CPU的元语实现。可以理解为直接操作二进制码。
1.9之前类的成员变量 只能用反射,反射每次操作之前都需要进行检查。
反射的效率低于VarHandle
1,普通属性原子操作
2,比反射快,直接操作二进制码
1,获取VarHandle
2,使用VarHandle操作
三,ThreadLocal
1,为什么是线程级的缓存?
// -Thread.currenmtThread.map(ThreadLocal,value)
//值设置在当前线程的map中
map是线程的map
2,用途
声明式事务,保证同一个Connection。
线程级的缓存