面试被问到关于并发锁的问题可以这样引入
先是辛苦啦奶子---到底层的锁升级--volitail---保持线程可见性--防止指令重排序---
---cas无锁优化 类 ----reetrainlock---countdownlatch ---cyclebarrier---readwritelock--- 信号量----交换
synchronized异常锁
只要程序发送异常锁就会被释放,可能应发程序乱入; 如果不想锁释放。那么catch捕获异常,让程序继续
锁可以升级不能降级 悲观锁
辛苦啦奶子的底层实现,第一个不加锁,第二个来的自旋锁,10次,适合执行时间不长的,最后搞不动了请求os, 重量级锁。进入等待队列 不消耗cpu 自旋是消耗cpu的
锁细化和粗化
并不是所有的代码都需要加锁 ,那么只锁需要加锁的地方, 当几乎所有地方都需要加锁的适合,不然直接全部加锁
用对象加锁,不能改变对象
这样线程拿的东西就不一样了,把对象加上final 大佬都是怎么做的,不要用string类型做为锁
CAS无锁优化 乐观锁
系统提供了一些cas实现的类 compare and set
cas 是cpu原语级别的 中间不能被 插入 ,先读出值,如果不是期望值 就在来读一次,然后进入循环
CAS ABA 问题
把1变为2 有变回去 好像什么都没用发生过
解决问题加版本号, 如果只是int类型无所谓
累加比较 无锁
longadd 分段锁
可重入锁 指同一个线程取拿自己的锁不用加锁
reententLock 一定要释放锁 写在fianlly里面
标准格式
新功能 尝试锁定 如果规定时间内没拿到就不要了
可以设置公平锁 谁先来谁先执行 辛苦啦奶子只有非公平锁 可被打断的
看这个线程上来会不会检查队列 默认是非公平锁
线程通信 countdownlatch
栅栏
到20个线程发车
栅栏的实际用处 就是并发执行一个实际需要等待所有结果出来
读写锁
是真的牛逼 如果加的是lock这中排他锁 就必然所有读的东西都需要等待 但是如果是读写锁 ,读可以并发读, 但是不能写
信号量
说人话就是限流 和线程池不同的是 他保证的同时只能最多K个线程运行,线程可以有无数个
而线程池可以有K个线程存在
acquire就是减少信号量
release就是是否信号量
如果要实现公平锁那你还先得实现先来后到不然都不用谈
exchange只能两个线程
执行两个exchange方法会阻塞
aqs 中最重要的
其实里面就是一个state和一个队列 队列是由node组成的 比如state是0 表示这个还没被抢 如果是1的话 就看你有木有抢到了,抢不到的话 就经入等待队列
countdownlatch底层也是cas
如果state不是0就卡着 ,是就放了
jdk1.9后加了varhandle
这个可以直接获取普通方法的属性 且比反射快
四大引用
- 强引用-----new 出来的 gc是不会回收
- 软引用----softrefrence gc在空间不够的时候会回收 适用于缓存
- 弱引用----weak 一般在容器里面用的多 threadlocal 里面用到了 gc看到就回收 ,不然的话就会发生内存泄漏
- 虚引用 ----堆外内存
容器分为map 和collection
hashtable 和vactor
发展历程
- hashtable 方法全家锁太慢了 就单论写效率 他还是比较快的 读太慢了
- hashmap 一个都不加锁 不安全
- collections.sycrenizemap() 帮hashpmap加锁 也不太行 比hashtable快了一丢丢 读写和hashtable差不多
- currenthashmap 写不是很快 但读还是很快的
多线程的一个一个加的 多考虑queue 他就是为了高平发准备的
底层是cas
并不是一定sync比cas慢
比如代码运行时间长,并发量不是很高 sync还是不错的选择
一些常用的map
因为实现concurrenttreemap 上用cas太复杂了,用跳表了来弥补
copyonwrite 用法 读特别多 写特别少的情况下 用
原理是读写都不加锁 写的时候赋值一份在加锁,除法你每次要读这个size
ConcurrentLinkedQueue 底层也是cas
queue家族
queue 就是peek poll offer 几个基本的不会阻塞的方法 加了block take 和put是会阻塞的 阻塞用的是pack 和await
delayqueue
倒计时任务的安排
syncqueue 线程交换 比exchange强 不用两边都阻塞 size为0
transferqueue
生产者 放完东西不许走。等着 由人来取走
excutor 接口 自己定义线程池怎么运行
execuorservice 定义了一些线程的生命周期 和提交方法
future 类储存call的结果
futuretask 牛逼大了 比call牛逼 既是一个任务 也自生可以储存结果
基本用不到的future---comparablefuture
并发组合各个多线程的结果
线程池
线程池 维护者一个线程队列 和一个任务队列
- 第一个参数 核心线程数 不会回收
- 第二个 最大线程数
- 第三个 空闲时间
- 第4个 单位时间
- 第四个 阻塞队列
- 第5个 线程工厂
- 第六个 当线程都忙且任务队列也满了 拒绝策略
拒绝策略
一般都是自定义
第三个讲一下:会扔掉最先进队列的任务
jdk自带的线程池 不建议用
可能会oom且 名字要自己命名
不能放在任务队列中 直接拿走
自己去估算要的线程数
线程池上来先启动核心线程 然后向队列里面扔,满了再启动非核心线程
workstealpool
不想线程池每个线程都有自己的队列,执行完自己的后 会去偷别人的
forkjoinPool
任务分片 类似于mapreduce