尚硅谷面试题第二季笔记
2021-12-02 15:53:49 0 举报
AI智能生成
尚硅谷面试题第二季笔记
作者其他创作
大纲/内容
1.1保证可见性
synchronized可保证原子性
1.2不保证原子性
体现JMM的有序性
1.3禁止指令重排
1.volatile是Java虚拟机提供的轻量级的同步机制(即轻量级的synchronized)
2.1可见性
使用原子类(java.util.concurrent.atomic)
为什么使用了原子类可以解决?原理是什么?CAS
number++在多线程下是非线程安全的,如何不加synchronized解决?
2.2原子性
2.3VolatileDemo代码演示可见性+原子性
重排1
案例
重排2
禁止指令重排小总结(了解)
2.4有序性
2.JMM你谈谈
线程安全获得保证
3.1单例模式DCL代码
3.2单例模式volatile分析
3.你在哪些地方用过volatile?
1.谈谈你对volatile的理解
1.比较并交换
atomicInteger.getAndIncrement();
Unsafe
自旋锁
unsafe.getAndAddInt
底层汇编
简单版小总结
CAS是什么
2.CAS底层原理?如果知道,谈谈你对Unsafe的理解
循环时间长开销大
只能保证一个共享变量的原子操作
通过原子引用解决ABA问题
引出来ABA问题???
3.CAS缺点
2.CAS你知道吗
ABA问题怎么产生的
原子引用
如何规避ABA问题
AtomicStampledReference
ABADemo
时间戳原子引用
3.原子类Atomiclnteger的ABA问题谈谈?原子更新引用知道吗?
解决方案1:Vector、Collections.synchronizedList
new CopyOnWriteArrayList<>()
限制不可以用Vector和Collections工具类解决方案2
4.我们知道ArrayList是线程不安全,请编写一个不安全的案例并给出解决方案
是什么
公平锁和非公平锁
ReentrantLock/Synchronized就是一个典型的可重入锁
可重入锁最大的作用是避免死锁
参考1
参考2
ReenterLockDemo
可重入锁(又名递归锁)
SpinLockDemo
UnSafe.getAndAddInt中的自旋锁
自旋的反义词就是阻塞
ReadWriteLockDemo
独占锁/共享锁
详见代码SyncAndReentrantLockDemo
synchronized和lock有什么区别?用新的lock有什么好处?你举例说说
5.公平锁/非公平锁/可重入锁/递归锁/自旋锁谈谈你的理解?请手写一个自旋锁
让一些线程阻塞直到另一个线程完成一系列操作后才被唤醒
CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,调用线程会被阻塞。其他线程调用countDown方法会将计数器减1(调用CountDown方法的线程不会阻塞),当计数器的值变为0时,因调用await方法被阻塞的线程会被唤醒,继续执行。
CountDownLatch倒计时
CyslicBarrier的字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是,让一组线程到达屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会打开门,所有被屏障拦截的线程才会继续干活,线程进入屏障通过CyclicBarrier的await()方法。
CyclicBarrierDemo
CyclicBarrier正向计时,直到某个值才
信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。
争车位
代码
SemaphoreDemo
Semaphore
6.CountDownLatch/CyclicBarrier/Semaphore
队列+阻塞队列
为什么用?有什么好处?
BlockingQueue的核心方法
架构介绍
ArrayBlockingQueue:由数组结构组成的有界阻塞队列
LinkedBlockingQueue:由链表结构组成的有界(但大小默认值为Integer.MAX_VALUE)阻塞队列。
PriorityBlockingQueue:支持优先级排序的无界阻塞队列。
DelayQueue:使用优先级队列实现的延迟无界阻塞队列。
理论
SychronousQueueDemo
SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列。(同步队列)
LinkedTransferQueue:由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:由链表结构组成的双向阻塞队列。
种类分析
架构梳理+种类分析
ProdConsumer_TraditionDemo
传统版
ProdConsumer_BlockQueueDemo
阻塞队列版
生产者消费者模式
线程池底层用的就是ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue
线程池
消息中间件
用在哪里
MQ的底层使用的就是阻塞队列
7.阻塞队列知道吗
为什么用线程池,优势
架构说明
Scheduled调度的意思
Executors.newScheduledThreadPool()
java8新增,使用目前机器上可用的处理器作为它的并行级别
Executors.newWorkStealingPool(int)
java8新出
了解
主要特点:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue。
执行长期的任务,性能好很多
Executors.newFixedThreadPool(int)
主要特点:创建一个单线程化的线程池,它只会唯一的工作线程来执行任务,保证所有任务按照指定顺序执行。newSingleThread将corePoolSize和maximumPoolSize都设置为1,它使用的是LinkedBlockingQueue。
一个任务一个任务执行的场景
Executors.newSingleThreadExecutor()
主要特点:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。newCachedThreadPool将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。
适用:执行很多短期异步的小程序或者负载较轻的服务器
Executors.newCachedThreadPool()
重点
编码实现
ThreadPoolExcutor
线程池如何使用?
1.corePoolSize:线程池中的常驻核心线程数
2.maximumPoolSize:线程池能够容纳同时执行的最大线程数,此值必须大于等于1.
3.keepAliveTime:多余的空闲线程的存活时间。当前线程池数量超过corePoolSize时,当空闲时间达到keepAliveTime值时,多余空闲线程会被销毁直到只剩下corePoolSize个线程为止。
4.unit: keepAliveTime的单位
5.workQueue:任务队列,被提交但尚未被执行的任务。
6.threadFactory:表示生成线程池中工作线程的线程工厂,用于创建线程一般用默认的即可。
AbortPolicy(默认):直接抛出RejectedExecutionException异常阻止系统正常运行。
CallerRunsPolicy:“调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。
DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务。
DiscardPolicy:直接丢弃任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种方案。
7.handler:拒绝策略,表示当队列满了并且工作线程大于等于线程池的最大线程数。
7大参数
线程池的几个重要参数介绍
1.在创建了线程池后,等待提交过来的任务请求。2.当调用execute()方法添加一个请求任务时,线程池会做如下判断: 2.1 如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务。 2.2 如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列。 2.3 如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务。 2.4 如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒线程无事可做绝策略来执行。3.当一个线程完成任务时,它会从队列中取下一个任务来执行。4.当一个线程超过一定的时间(keepAlilveTime)时,线程池会判断: 如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。 所以线程池的所有任务完成后它最终会收缩到corePoolSize的大小。
说说线程池的底层工作原理?
8.线程池用过吗?ThreadPoolExecutor谈谈你的理解?
JDK内置的拒绝策略
以上内置拒绝策略均实现了RejectedExecutionHandler接口
线程池的拒绝策略你谈谈
答案是一个都不用,我们生产上只能使用自定义的
Executors中JDK已经给你提供了,为什么不用?
你在工作中单一的/固定数的/可变的单重创建线程池的方法,你用哪个多?超级大坑
Case:MyThreadPoolDemo
你在工作中是如何使用线程池的,是否自定义过线程池使用
CPU密集型
1
2
IO密集型
合理配置线程池你是如何考虑的?
9.线程池用过吗?生产上你是如何设置合理参数?
系统资源不足
进程运行推进的顺序不合适
资源分配不当
产生死锁的主要原因
代码:DeadLockDemo
jps命令定位进程号
jstack找到死锁查看
解决
10.死锁编程及定位分析
11.Java里面锁请谈谈你的理解,能说多少说多少
3.JUC多线程及高并发
JVM体系概述
Java8以后的JVM
JVM内存结构
GC的作用域
引用计数
用于新生代
复制
标记清除会导致内存碎片(用于老生代)
标记清除
不让费空间又不产生碎片,但耗费时间比较多(用于老生代)
标记整理
常见的垃圾回收算法
前提复习
节外生枝补充知识
内存中已经不再被使用到的空间就是垃圾
什么是垃圾
引用计数法
case
虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。
方法区中的类静态属性引用的对象。
方法区中常量引用的对象
本地方法栈中JNI(Native方法)引用的对象。
Java中可以作为GC Roots的对象
枚举根节点做可达性分析(根搜索路径)
要进行垃圾回收,如何判断一个对象是否可以被回收?
1.JVM垃圾回收的时候如何确定垃圾?是否知道什么是GC Roots
-version
-help
java -showversion
标配参数
-Xint:解释执行
-Xcomp:第一次使用就编译成本地代码
-Xmixed:混合模式
x参数(了解)
-XX:+或者-某个属性值
+表示开启 -表示关闭
公式
-XX:-PrintGCDetails
-XX:+PrintGCDetails
是否打印GC收集细节
-XX:-UseSerialGC
-XX:+UseSerialGC
是否使用串行垃圾回收器
Case
Boolean类型
-XX:属性key=属性值value
-XX:MetaspaceSize=128m
-XX:MaxTenuringThreadhold=15
KV设值类型
jinfo -flag 配置项 进程编号
jinfo举例,如何查看当前运行程序的配置
两个经典参数:-Xms和-Xmx
-Xms:等价于-XX:InitialHeapSize
-Xmx:等价于-XX:MaxHeapSize
这个你如何解释
题外话(坑题)
xx参数
JVM的参数类型
主要查看初始默认
java -XX:+PrintFlagsInitial -version
java -XX:+PrintFlagsInitial
-XX:+PrintFlagsInitial
主要查看修改更新
java -XX:+PrintFlagsFinal -version
java -XX:+PrintFlags -version
-XX:+PrintFlagsFinal
p class=\"p1\" style=\
-XX:+PrintCommandLineFlags
盘点家底查看JVM默认值
2.你说你做过JVM调优和参数配置,请问如何查看JVM系统默认值
基础知识
初始化大小内存,默认为物理内存1/64
等价于-XX:InitialHeapSize
-Xms
最大分配内存,默认物理内存的1/4
等价于-XX:MaxHeapSize
-Xmx
设置单个线程栈的大小,一般默认为512K~1024K
等价于-XX:ThreadStackSize
-Xss
设置年轻代大小
-Xmn
元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:永久代在堆里面元空间并不在虚拟机中,而是使用本地内存。因此,在默认情况下,元空间的大小仅受本地内存限制
Xms10m -Xmx10m -XX:MetaspaceSize=1024m -XX:+PrintFlagsFinal
-XX:MetaspaceSize
典型设置案例
GC
FullGC
-XX:SurvivorRatio
-XX:NewRatio
设置垃圾的最大年龄
-XX:MaxTenuringThreshold
常用参数
3.你平时工作用过的JVM常用基本配置参数有哪些?
Case:StackOverflowErrorDemo
java.lang.StackOverFlowError
Case:JavaHeapSpaceDemo
java.lang.OutOfMemoryError:Java heap space
Case:GCOverheadDemo
java.lang.OutOfMemoryError:GC overhead limit exceeded
Case:DirectBufferMemoryDemo
java.lang.OutOfMemoryError:Direct buffer memory
使用java -XX:+PrintFlagsInitial命令查看本机的初始化参数,-XX:Metaspacesize为218103768(大约为20.8M)
java.langOutOfMemoryError:Metaspace
非root用户登录linux系统测试
服务器级别调参调优
java.lang.OutOfMemoryError:unable to create new native thread
4.请谈谈你对OOM的认识?
GC算法(引用计数/复制/标清/标整)是内存回收的方法论,垃圾收集器就是算法落地实现。
因为目前为止还没有完美的收集器出现,更加没有万能的收集器,只是针对具体应用最合适的收集器,进行分代收集。
它为单线程环境设计且只使用一个线程进行垃圾回收,会暂停所有的用户线程。所以不适合服务器环境。(程序->GC->程序)
串行垃圾回收器(Serial)
多个垃圾收集线程并行工作,此时用户线程是暂停的,适用于科学计算/大数据处理平台处理等弱交互场景。
并行垃圾回收器(Parallel)
用户线程和垃圾收集线程同时执行(不一定是并行,可能交替执行),不需要停顿用户线程,使用对响应时间有要求的场景。
并发垃圾回收器(CMS)( Concurrent Mark Sweep)
上述3个小总结,G1特殊后面说
G1垃圾回收器将堆内存分割成不同的区域然后并发的对其进行垃圾回收。
GI垃圾回收器
4种主要垃圾收集器
5.GC回收算法和垃圾收集器的关系?分别是什么请你谈谈
怎么查看默认的垃圾收集器是哪个?
默认的垃圾收集器有哪些?
Default New Generation
DefNew
Old
Tenured
Parallel New Generation
ParNew
Parallel Scavenge
PSYoungGen
Parallel Old Generation
ParOldGen
部分参数预先说明
Server/Client模式分别是什么意思
新生代和老年代都是只有一条线程
串行GC(Serial)/(Serial Copying)
新生代多个并行清理垃圾,老年代还是一个
并行GC(ParNew)
新生代和老年代都是多个一起清理垃圾
并行回收GC(Parallel)/(Parallel Scavenge)
新生代
串行GC(Serial Old)/(Serial MSC)
子主题
并行GC(Parallel Old)/(Parallel MSC)
初始标记(CMS initial mark)
并发标记(CMS concurrent mark)和用户线程一起
重新标记(CMS remark)
并发清除(CMS concurrent sweep)和用户线程一起
4步过程
并发收集低停顿
优点
并发执行,对CPU资源压力大
采用的标记清除算法会导致大量碎片
缺点
优缺点
并发标记清除GC(CMS)
老年代
垃圾收集器配置代码总结
垃圾收集器
如何选择垃圾收集器
6.怎么查看服务器默认的垃圾收集器是哪个?生产上你是如何配置垃圾收集器的?谈谈你的理解?
年轻代和老年代是各自独立且连续的内存块
年轻代收集使用单eden+S0+S1进行复制算法
老年代收集必须扫描整个老年代区域
都是以尽可能少而快速地执行GC为设计原则。
以前收集器特点
特点
G1是什么
最大好处是化整为零,避免全内存扫描,只需要按照区域来进行扫描即可。
Region区域化垃圾收集器
回收步骤
底层原理
case案例
-XX:+UseG1GC
-XX:G1HeapRegionSize=n:设置的G1区域的大小。值是2的幂,范围是1MB到32MB。目标是根据最小的Java堆大小划分出约2048个区域。
-XX:MaxGCPauseMillis=n:最大GC停顿时间,这是个软目标,JVM将尽可能(但不保证)停顿小于这个时间。
-XX:InitiatingHeapOccupancyPercent=n:堆占用了多少的时候就触发GC,默认为45
-XX:ConcGCThreads=n:并发GC使用的线程数。
-XX:G1ReservePercent=n:设置作为空闲的预留内存百分比,以降低目标空间溢出的风险,默认值是10%
常用配置参数(了解)
和CMS相比的优势
小总结
7.G1垃圾收集器
整体架构
Case:StrongReferenceDemo
强引用(默认支持模式)
Case:SoftReferenceDemo
软引用
Case:WeakReferenceDemo
软引用和所引用的使用场景
Case:WeakHashMapDemo
你知道所引用的话,能谈谈weakHashMap吗
弱引用
case:ReferenceQueueDemo
引用队列
case:PhantomReferenceDemo
虚引用
GCRoots和四大引用小总结
8.强引用、软引用、弱引用、虚引用分别是什么?
uptime,系统性能命令的精简版
整机:top
查看CPU(包含不限于)
mpstat -P ALL 2
查看所有cpu核信息
pidstat -u1 -p进程编号
每个进程使用cpu的用量分解信息
查看额外
vmstat
CPU:vmstat
应用程序可用内存数
pidstat -p 进程号 -r 采样间隔秒数
内存:free
查看磁盘剩余空间数
硬盘:df
磁盘I/O性能评估
pidstat -d采样间隔秒数 -p 进程号
磁盘IO:iostat
默认本地没有,下载ifstat
查看网络IO
网络IO:ifstat
9.生产环境服务器变慢,诊断思路和性能评估谈谈?
结合Linux和JDK命令一块分析
1.先用top命令找出CPU占比最高的
2.ps -ef或者jps进一步定位,得知是一个怎么样的一个后台程序惹事
-m显示所有的线程
-p pid进程使用cpu的时间
-o该参数后是用户自定义格式
参数解释
3.定位到具体线程或者代码
printf \"%x\\"有问题的线程ID
4.将需要的线程ID转换为16进制格式(英文小写格式)
5.jstack进程ID | grep(16进制线程ID小写英文) -A60
案例步骤
10.假设生产环境出现CPU占用过高,请谈谈你的分析思路和定位
概览
官网
解释
jps
jinfo
jmap
jstat
jstack
性能监控工具
11.对于JDK自带的JVM监控和性能分析工具用过哪些?一般你是怎么用的?
题目1
4.JVM+GC解析
尚硅谷面试题第二季
收藏
0 条评论
回复 删除
下一页