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