本期内容包括
JUC多线程并发、JVM和GC等目前大厂笔试中会考、面试中会问、工作中会用的高频难点知识。
斩offer、拿高薪、跳槽神器,对标阿里P6的《尚硅谷_互联网大厂高频重点面试题(第2季)》发布。本套课程总结分析了2019年大厂互联网公司常见常考的技术点,通过对40多个题目共计120集视频详细全面的讲解,让大家深刻掌握、扎实吃透当前的主流Java高级技术。
NoSQL数据库Redis
消息中间件MQ
JUC多线程及高并发
并发和并行有什么区别
并发:多个线程去访问同一个资源
并行:各种事情同时去做,一边干什么,一边干什么
请谈谈你对volatile的理解
volatile是什么
三大特性
JMM内存模型之可见性
JMM:java内存模型
各个线程对主内存中的数据进行改变,不是直接修改,而是会把age=25拷贝到自己的工作内存中再进行改变
t1改为37后要把新数据写回到主内存中,t2,t3不知道主内存中的值已经改变了
所以我们需要有一个机制:JMM内存模型的可见性,只要有一个线程改变数据后要写回到主内存中,其它的线程马上就会知道主内存中的数据已经改变了
可见性的代码验证说明
3秒之后,a线程已经把number改了,但是main线程不知道,对main线程不可见,还在傻傻的等着,没有人通知我
现在修改程序,加了volatile
增加了内存之间的可见性
volatile不保证原子性
原子性:不可分割、完整性,也就是某个线程正在做某个具体业务时,中间不可以被加塞或者被分割,需要整体完整,要么同时成功,要么同时失败。
把验证可见性的代码写到一个方法seeokbyvolatile
20个线程去调用addplus方法,每个线程调用1000次,则结果应该是2000。
多线程编程的模板forthread10是自己设置的快捷键
volatile不保证原子性理论解释
轻量级就是乞丐版的sunchronized
不要杀鸡焉用牛刀,太重了
拷贝回自己的内存空间,每个人都拿到0,写回到主内存时,线程1写回到的时候被挂起了,线程2歘的写回了。然后线程1恢复后又写回了一遍,把原来的1给覆盖了。
三个线程都拿到1,都在各自的工作内存中加1,写回到的时候,没有拿到最新的值就又写了,写覆盖
volatile不保证原子性问题解决
volatile指令重排案例1
不可以,必须考虑指令之间的数据依赖性
加了volatile之后是禁止指令重排
volatile指令重排案例2
答案是6
但是指令重排后,答案可能是5
所以在这些变量前面加上volitaile就可以禁止指令重排
单例模式在多线程环境下可能存在安全问题
单例模式有六种
掌握一种即可
第一步 定义私有的实例变量
第二步 构造方法
第三步 新建,返回同一个变量
比较引用类型,内存地址
在多线程坏境下,单例模式出现了问题
如果加上synchronized,在多线程的环境控制住了
但是太重了,并发性下降了
单例模式volatile分析
DCL模式 Double Check Lock双端检锁机制
在加锁之前和之后都进行一次检测
地址不为空,但是内容为空
所以要在instance变量上面加上volatile
总结:
CAS你知道吗?
CAS是什么
CAS compare and set 比较并交换
多个线程去操作主内存中的数据。
一个叫做期望值、一个叫做更新值
主内存中数据是5
一个线程拷贝回去自己的工作内存,对它进行修改,然后写回到主内存的时候,会进行比较和交换,如果和拷贝的数据一样的话,就将改变后的数据写回去;否则的话,就不进行写回。
CAS底层原理上
当前对象 内存偏移量
AtomicInteger 的getandincrement方法底层其实是CAS思想+套的是unsafe类的CPU原语来保证原子性,
底层思想是比较并交换,真实值和期望值相等就交换成功,否则就失败,失败就再来,直到比较成功为止。
CAS底层原理下
CAS缺点
CAS能够不加锁保证一致性,并发性加强了
原子类AtomicInteger的ABA问题谈谈?原子更新引用知道吗?
ABA问题
2号线程比1号线程快,把原来的A改为B,又改为A。
1号线程回来后,期望的和原来的一样,以为没有改变过,于是写回主内存。
但是中间有猫腻,2号线程已经把它改过了又改回去了。
AtomicReference原子引用
只管开头和结尾,没有管中间。
原子引用的泛型类
原子User类型
AtomicStampedReference版本号原子引用
时间戳原子引用
ABA问题的解决
修改成功,
我们知道ArrayList是线程不安全的,请编码写一个不安全的案例并给出解决方案。
集合类不安全之并发修改异常
ArrayList底层是一个数组
什么类型的数组?
数组初始长度为10,超过10以后进行扩容。
它是线程不安全的。
举一个线程不安全的案例。
把3改为30
java.util.ConcurrentModificationException
并发修改异常
线程不安全是指:在多线程的情况下,
方法一:
Vector类可以解决这个问题,加锁一致性可以保证,但是并发性急剧下降。
不许用Vector
方法二:
Map和Set也是不安全的集合类
方法三:
集合类不安全之写时复制
写时复制
锁、扩容、写新元素
集合类不安全之Set
集合类不安全之Map
TransferValue醒脑小练习
传值还是传引用
age属于main方法的,然后调用方法时复印了一份传给它,然后方法把复印件给改动了
我只是给你复印了一份值,原件根本没动,所以第一个age还是20
person是main的,传引用传内存地址给方法,两个引用指向了同一个地址,这时把这个地址的值改动了
str是属于main方法的,这个池子里有了abc
这个池子里面没有xxx,那么就重新创建一个指向它
公平锁/非公平锁/可重入锁/递归锁/自旋锁 谈谈你的理解?请手写一个自旋锁。
Java锁之公平锁/非公平锁
无参数:不公平 允许某个同学突然出来加塞
有参数:公平 队列先来后到
synchronized等同于锁,非公平锁
被它抢到了锁上了
Java锁之可重入锁和递归锁理论
Java锁之可重入锁和递归锁代码验证
资源类
线程操作资源类
t1线程在外层方法获取锁的时候,t1在进入内层方法会自动获取锁
只要锁匹配,几把锁都可以。
Java锁之自旋锁理论知识
Unsafe类+CAS思想(自旋锁)
Java锁之自旋锁代码验证
如果是第一次进来线程,就不进循环
A线程进来,发现期望的是空的,那么while的条件就是false,于是不进入循环,直接拿到了锁。
B线程进来,发现期望的值不是空,那么while的条件就是true,于是它进入锁中,一直会循环的判断,直到期望的值是空了,才能推出循环,获得锁。
BB等5秒钟,等A解锁了,B才能解锁
Java锁之读写锁理论知识
Java锁之读写锁代码验证
以前使用锁和synchronized读和写通通不能并发执行,数据一致量可以保证,但并发性急剧下降。
线程操作资源类
高内聚低耦合
锁不能进行细粒度的划分,只能把全部进行封杀
CountDownLatch/CyclicBarrier/Semaphore使用过吗?
CountDownLatch
火箭发射
使用枚举
CyclicBarrier Demo
循环屏障
人都到齐了才能开会
一个是减法,倒计时
一个是加法,累积
Semaphore Demo
多个线程强多个资源
信号灯
资源为1时就退化成synchronized
不写是非公平锁,效率高允许加塞
可以复用
可以抢占可以释放
停3秒钟
阻塞队列知道吗?
阻塞队列理论
阻塞队列接口结构和实现类
生产者 消费者 sychronized wait notify
队列:先进先出
阻塞:
生产者消费者模式:用阻塞对垒写
阻塞队列API之抛出异常组
检查队列的首元素是谁?
a
先进先出
阻塞队列API之返回布尔值组
队列的首元素是谁
阻塞队列API之阻塞和超时控制
阻塞的意思是:我现在满了,就等着,直到有元素出去。因为我不能丢消息呀,就等着
取不出来就堵着
过时不候
超时
这时候没有等2秒钟
只阻塞2秒钟。2秒钟之后就打印出false
阻塞队列之同步SynchronizedQueue队列
0库存,生产出来的马上被别人拿走
你不消费,你想到里面插第二个你插不进去
线程通信之生产者消费者传统版
防止虚假唤醒
没有控制住,生产了2个
所以唤醒的时候要加入循环
synchronized和lock有什么区别
锁绑定多个条件condition
线程通信之生产者消费者阻塞队列版
线程池用过吗?ThreadPoolExecutor谈谈你的理解?
Callable接口
区别:
runnable接口没有返回值,不会抛异常,实现run
callable接口有返回值,会抛异常,实现call
要的是runnable,有的是callable,如何把它们之间加上关系
生活的例子:两个同学,我默认接收的参数是张,但是,要找一个中间人
FutureTask实现穿针引线的作用。
已经有runnable接口,为什么还需要callable接口呢?
多个线程都要使用cllable,每次都要返回一个成功或者失败的返回值。
适配模式。
get()建议放在最后
因为我们不会等这个线程,给它充足的时间去计算
如果把get放到前面,mian线程就被堵住了
所以这里可以加一个循环的判断!等算完了,才往下做
两个线程都开始做同一个任务,只会执行一次!即复用
如果说非要进去!那么就要启动多个futuretask
线程池使用及优势
永远传参数传接口
看CPU的核数
线程池3个常用使用方式
ThreadPoolExecutor
辅助工具类
一池固定线程、一池一线程、一池多线程
线程池7大参数入门简介
线程池7大参数深入介绍
线程池底层工作原理
线程池用过吗?生产上你如何设置合理参数。
线程池的4种拒绝策略理论简介
从而降低任务流量。
线程池实际中使用哪一个
无界的阻塞队列
线程池的手写改造和拒绝策略
最高几个客户来办业务,就报拒绝了
默认的拒绝策略:马上报异常
调用者运行机制:不会抛弃任务也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量
谁让你调用我的,那么你就去找他
抛弃队列中等待时间最久的任务
这样的话只能完成8个人的业务,剩下的就直接丢弃了
线程池配置合理线程数
死锁编码及定位分析
单线程不可能有死锁!没人跟你抢,你怎么会死锁。
两把锁
持有自己的锁还妄图得到别人的锁。