高平发和锁

171 阅读5分钟

面试被问到关于并发锁的问题可以这样引入

先是辛苦啦奶子---到底层的锁升级--volitail---保持线程可见性--防止指令重排序---

---cas无锁优化 类 ----reetrainlock---countdownlatch ---cyclebarrier---readwritelock--- 信号量----交换

synchronized异常锁

只要程序发送异常锁就会被释放,可能应发程序乱入; 如果不想锁释放。那么catch捕获异常,让程序继续

锁可以升级不能降级 悲观锁

辛苦啦奶子的底层实现,第一个不加锁,第二个来的自旋锁,10次,适合执行时间不长的,最后搞不动了请求os, 重量级锁。进入等待队列 不消耗cpu 自旋是消耗cpu的

锁细化和粗化

并不是所有的代码都需要加锁 ,那么只锁需要加锁的地方, 当几乎所有地方都需要加锁的适合,不然直接全部加锁

用对象加锁,不能改变对象

这样线程拿的东西就不一样了,把对象加上final 大佬都是怎么做的,不要用string类型做为锁

CAS无锁优化 乐观锁

系统提供了一些cas实现的类 compare and set

cas 是cpu原语级别的 中间不能被 插入 ,先读出值,如果不是期望值 就在来读一次,然后进入循环

image.png

image.png

CAS ABA 问题

把1变为2 有变回去 好像什么都没用发生过

解决问题加版本号, 如果只是int类型无所谓

image.png

累加比较 无锁

longadd 分段锁 image.png

可重入锁 指同一个线程取拿自己的锁不用加锁

reententLock 一定要释放锁 写在fianlly里面

标准格式

image.png

新功能 尝试锁定 如果规定时间内没拿到就不要了

image.png

可以设置公平锁 谁先来谁先执行 辛苦啦奶子只有非公平锁 可被打断的

看这个线程上来会不会检查队列 默认是非公平锁 image.png

线程通信 countdownlatch

image.png

栅栏

到20个线程发车

image.png

栅栏的实际用处 就是并发执行一个实际需要等待所有结果出来

image.png

  读写锁

是真的牛逼 如果加的是lock这中排他锁 就必然所有读的东西都需要等待 但是如果是读写锁 ,读可以并发读, 但是不能写

image.png

信号量

说人话就是限流 和线程池不同的是 他保证的同时只能最多K个线程运行,线程可以有无数个

而线程池可以有K个线程存在 acquire就是减少信号量 release就是是否信号量 image.png

如果要实现公平锁那你还先得实现先来后到不然都不用谈

 exchange只能两个线程

执行两个exchange方法会阻塞

image.png

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 写不是很快 但读还是很快的 image.png

多线程的一个一个加的 多考虑queue 他就是为了高平发准备的

image.png

底层是cas

并不是一定sync比cas慢

比如代码运行时间长,并发量不是很高 sync还是不错的选择

一些常用的map

image.png

因为实现concurrenttreemap 上用cas太复杂了,用跳表了来弥补

image.png

copyonwrite 用法 读特别多 写特别少的情况下 用

原理是读写都不加锁 写的时候赋值一份在加锁,除法你每次要读这个size

ConcurrentLinkedQueue    底层也是cas

queue家族

queue 就是peek poll offer 几个基本的不会阻塞的方法 加了block take 和put是会阻塞的 阻塞用的是pack 和await

image.png

image.png

delayqueue

倒计时任务的安排

image.png

syncqueue 线程交换 比exchange强 不用两边都阻塞 size为0

image.png

transferqueue

生产者 放完东西不许走。等着 由人来取走

image.png

excutor 接口 自己定义线程池怎么运行

execuorservice 定义了一些线程的生命周期 和提交方法

future 类储存call的结果

futuretask 牛逼大了 比call牛逼 既是一个任务 也自生可以储存结果

image.png

基本用不到的future---comparablefuture

并发组合各个多线程的结果

image.png

线程池

image.png

线程池 维护者一个线程队列 和一个任务队列

image.png

  • 第一个参数 核心线程数 不会回收
  • 第二个 最大线程数
  • 第三个 空闲时间
  • 第4个 单位时间
  • 第四个 阻塞队列
  • 第5个 线程工厂
  • 第六个 当线程都忙且任务队列也满了 拒绝策略

拒绝策略

一般都是自定义

image.png 第三个讲一下:会扔掉最先进队列的任务

image.png

jdk自带的线程池 不建议用

image.png

可能会oom且 名字要自己命名

image.png 不能放在任务队列中 直接拿走 image.png

image.png 自己去估算要的线程数

image.png

线程池上来先启动核心线程 然后向队列里面扔,满了再启动非核心线程

workstealpool

不想线程池每个线程都有自己的队列,执行完自己的后 会去偷别人的

image.png

image.png

forkjoinPool

任务分片 类似于mapreduce

image.png

jhm

image.png

distropt

image.png

image.png

总结

image.png