面试常见:
Jvm,高并发,多线程,数据库,redis,框架
多线程
1.线程和进程是什么?如何保证线程安全性?
进程线程(一)——基础知识,什么是进程?什么是线程?_什么是进程什么是线程-CSDN博客
java创建线程(Thread)的5种方式_java new thread-CSDN博客
进程是操作系统最小的资源单位,线程是任务的最小单位(程序执行的最小单位)。
线程的开启方式(5种):
- 方式一:继承于Thread类
- 方式二:实现Runnable接口
- 方式三:实现Callable接口
- 方式四:使用线程池
- 方式五:使用匿名类
保证线程安全的方式(3种):
1.同步代码块
2.同步方法
3.Lock锁机制, 通过创建Lock对象,采用lock()加锁,unlock()解锁,来保护指定的代码块
针对造成线程不安全的问题进行保证的方法(3种):
1.针对原子性 -- 使用synchronized 加锁
2.针对可见性 -- 使用synchronized 关键字加锁
-- 使用volatile 关键字,要优于 synchronized 的性能,同样可以保证修改对其他线程的可见性。
3.针对重排序性 -- 通过 synchronized 关键字定义同步代码块或者同步方法保障有序性,另外也可以通过 Lock 接口来保障有序性。
2.volatile 和 synchronized 的区别
一文彻底搞懂 volatile 和 synchronized 的区别-CSDN博客
volatile
1.主要是用于保证共享爱变量的可见性,只能防止指令重排序,不能保证原子性和安全性
2.适用于只有一个线程写,多个线程读的场景(标识位,状态标志)
3.性能开销低
synchronized
1.不仅保证了可见性,还保证了原子性和有序性
2.适用于临界区代码(对共享资源的读写操作)
3.性能开销高
3.偏向锁 轻量级锁 重量级锁的优化机制
锁的优化机制(偏向锁、自旋锁、轻量级锁、重量级锁)_偏向锁 轻量级锁 重量级锁-CSDN博客
1.锁的升级过程从低到高依次为 无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁
降级在一定条件也是有可能发生的
优化机制包括自适应锁、自旋锁、锁消除、锁粗化、轻量级锁和偏向锁。
偏向锁
1.永远偏向第一个获得锁的线程
2.当偏向锁的获取出现竞争,则偏向锁可能会升级为轻量级锁,偏向锁适用于无竞争、竞争小的场景。
3.偏向锁基本实现
对于synchronized,偏向锁就用设置-XX:+UseBiasedLocking开启偏向锁
对于ReentrantLock,new ReentrantLock(false)(非公平锁)设置参数为false创建的是偏向锁
自旋锁
1.等待获取到锁后再进行下一步操作
2.自旋转锁基本实现
对于synchronized,自旋锁可以通过设置-XX:+UseSpining来开启,自旋的默认次数是10次,可以使用-XX:PreBlockSpin设置。
对于ReentrantLock,通过逻辑设置忙等待处理
轻量级锁
1.在轻量级锁状态下继续锁竞争,如果成功就成功获取轻量级锁
2.如果锁竞争情况严重,某个达到最大自旋次数的线程,会将轻量级锁升级为重量级锁
3.轻量级锁基本实现
当synchronized所修饰的方法所访问的对象是静态对象时,确实可以认为它是轻量级锁。
ReentrantLock默认使用的是轻量级锁,在锁竞争的情况下,ReentrantLock 会将轻量级锁升级为重量级锁,以避免锁竞争和提高性能。
重量级锁
1.重量级锁即是需要排队竞争锁,当锁被占用时则需要挂起,等待锁释放后唤醒线程竞争获取锁
2.重量级锁基本实现
当使用 synchronized 关键字时,如果锁对象是一个实例对象,那么就是重量级同步。
如果锁被其他线程占用,那么 ReentrantLock 会将轻量级锁升级为重量级锁,从而避免锁竞争,提高性能。
锁粗化
锁粗化指的是有很多操作都是对同一个对象进行加锁,就会把锁的同步范围扩展到整个操作序列之外
锁消除
锁消除指的是JVM检测到一些同步的代码块,完全不存在数据竞争的场景,也就是不需要加锁,就会进行锁消除
计算机网络
1.TCP和UTP的区别,TCP为什么是3次握手
TCP 和 UTP 有什么区别?_utp与tcp-CSDN博客
一文读懂TCP的三次握手(详细图解)_tcp三次握手图解-CSDN博客
TCP
1.传输控制协议,面向连接可靠的传输通信协议
2.高可靠,效率低
UTP
用户数据报协议,是一种无连接不可靠的传输层通信协议
2.不可靠,效率高
第一次握手是客户端发送请求
第二次握手是服务器确认请求
第三次握手是客户端确认服务端确认客户端
TCP三次握手的优点
1.阻止了历史连接,可靠
2.避免反复确认,浪费资源
2.Http和Https的区别
HTTP 与 HTTPS 的区别 | 菜鸟教程 (runoob.com)
- HTTP 明文传输,数据都是未加密的,安全性较差,HTTPS(SSL+HTTP) 数据传输过程是加密的,安全性较好。
- 使用 HTTPS 协议需要到 CA(Certificate Authority,数字证书认证机构) 申请证书,一般免费证书较少,因而需要一定费用。证书颁发机构如:Symantec、Comodo、GoDaddy 和 GlobalSign 等。
- HTTP 页面响应速度比 HTTPS 快,主要是因为 HTTP 使用 TCP 三次握手建立连接,客户端和服务器需要交换 3 个包,而 HTTPS除了 TCP 的三个包,还要加上 ssl 握手需要的 9 个包,所以一共是 12 个包。
- http 和 https 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。
- HTTPS 其实就是建构在 SSL/TLS 之上的 HTTP 协议,所以,要比较 HTTPS 比 HTTP 要更耗费服务器资源。
Java I/O模型
1.I/O模型的模式
Java I/O 模型详解:BIO、NIO 与 AIO 的特性与应用_aio应用场景-CSDN博客
Java 中的 I/O 操作主要包括三种模式:BIO(阻塞 I/O)、NIO(非阻塞 I/O)和 AIO(异步 I/O)
- BIO(Blocking I/O):适合简单、少量连接的应用场景,编程简单但性能较低。
- NIO(Non-blocking I/O):适合高并发、大量连接的应用,使用复杂但性能较好。
- AIO(Asynchronous I/O):适合超高并发、长连接的应用,异步非阻塞,性能最佳
1. NI/O有什么核心组件?
Java NIO 基本原理以及三大核心组件_java nio核心组件有哪些-CSDN博客
Java NIO 的三大核心组件。分别是:Channel(通道),Buffer(缓冲区),Selector(选择器),Selector 能让单线程同时处理多个客户端 Channel,非常适用于高并发,传输数据量较小的场景。 NIO 基于 Channel 通道和 Buffer 缓冲区进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择器)用于监听多个通道的事件(如连接打开、数据到达)。因此单个线程可以监听多个数据通道。
3. Select Poll Epoll区别
http://t.csdnimg.cn/L6jHm
1.最大连接数
select:单个进程连接数最大是32个整数
poll:没有最大连接数的限制,原因是它是基于链表来存储
epoll:虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接
2.效率问题
select:每次调用时都会对连接进行线性遍历,效率低
poll:同上
epoll:在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。
3.消息传递方式
select:内核需要将消息传递到用户空间,都需要内核拷贝动作
poll:同上
epoll:epoll通过内核和用户空间共享一块内存来实现的。
JVM
大白话带你认识 JVM | JavaGuide
1.Jvm的结构
说不清 看网站吧(没有摆烂)
Java内存区域详解(重点) | JavaGuide
2.Java类加载的全过程
http://t.csdnimg.cn/foByX
一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)七个阶段,其中验证、准备、解析三个部分统称为链接(Linking)。
3.JVM中一个对象从加载到jvm开始到GC回收,经历的过程是什么
JVM基础 -> ⼀个对象从加载到JVM,再到被GC清除,都经历了什么过程?_普通对象到第一次gc经理什么-CSDN博客
⼀个对象从加载到JVM,再到被GC清除,都经历了什么过程?
⾸先类加载器把
字节码⽂件内容加载到⽅法区
,当然类加载器这中间用双亲委派机制
加载然后再根据加载完方法区中的类信息在堆区
为对象分配内存丶初始化零值丶设置对象头执行 init 方法
- 分配内存: 确定大小的内存从 Java 堆中划分出来
- 初始化零值: 将分配到的内存空间都初始化为零,这样对象只定义,不初始化也可以用
- 设置对象头: 对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等
- 执行 init 方法: 最后就是调用构造方法了,初始化对象,完成创建
对象⾸先会分配在
堆内存中新⽣代的Eden
。
- 小对象分配在
新⽣代的Eden
。然后经过新生代GC
,对象如果存活,就会进⼊S区。
- 在后续的每次GC中,如果对象⼀直存活,就会在S区来回拷⻉,每移动⼀次,年龄加1。
- 多⼤年龄才会移⼊⽼年代? 年龄最⼤15, 超过⼀定年龄后,对象转⼊⽼年代。
- 对象够大,分配在
老年代
- JDK 6 之前存在空间担保 -> 老年代保证我会保留一个连续内存大小的内存空间
- 如果内存不够,你就可以FullGC,还不够内存溢出
- 如果内存够,那就存进去
- JDK 6 之后 -> 老年代的连续空间大于
新生代对象总大小或者历次晋升的平均大小(动态年龄)
- 如果内存不够,你就可以FullGC,还不够内存溢出
- 如果内存够,那就存进去
当创建对象的⽅法执⾏结束后,栈中的指针会先移除掉了,对象就没有
GC Roots
根节点的引用了然后GC根据
可达性分析法
,判断对象是否可以被回收最后GC线程
调用合适的GC算法
清理掉可回收的对象
总结
1.创建对象的时候,jvm在方法区寻找对象相关信息,然后创建对象
2.在堆里面创建对象,是半初始化状态
3.对象首先会分配在堆内存中新生代Eden
4.当创建对象的⽅法执⾏结束后,栈中的指针会先移除掉了,对象就没有
GC Roots
根节点的引用了5.然后GC根据
可达性分析法
,判断对象是否可以被回收6.最后GC线程
调用合适的GC算法
清理掉可回收的对象
4.怎样确定这个对象是否被回收
Java垃圾回收机制(如何判断一个对象是否该回收)_3.java垃圾收集机制,并描述垃圾收集器如何判断一个对象是否能被收集。-CSDN博客
1.引用计数法
给对象增加一个计数器,当它被引用时,计数器就加一,引用失效时,计数器就减一,如果计数器为0,等于垃圾,被回收。
2.可达性分析算法
这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的,需要被回收。
5.Jvm有哪些垃圾回收算法
JVM的4种垃圾回收算法、垃圾回收机制与总结_jvm垃圾回收机制有几种-CSDN博客
1.标记清除算法
2.标记复制算法(拷贝算法)
3.标记整理算法
4.分代收集算法
5.增量收集算法
6.分区算法
6.什么是STW,我们能避免它吗?
JVM的4种垃圾回收算法、垃圾回收机制与总结_jvm垃圾回收机制有几种-CSDN博客
1.我们不能避免STW
2.STW,即
Stop-The-World
的缩写,指的是系统在执行特定操作时需暂停(停止)所有应用程序线程。3.JVM对垃圾回收站进行算法处理的时候,STW是把jvm内存冻结的一种状态,在这个状态下,java所有线程都是停止状态,除了GC。
7.如何进行jvm调优,jvm的调优参数有哪些?
http://t.csdnimg.cn/VwdK1
1、堆内存调优
使用场景:调整JVM的堆内存可以帮助避免内存溢出,提高垃圾回收的效率。
-Xms 参数用于设置JVM启动时的初始堆大小。
-Xmx 参数用于设置JVM可以使用的最大堆内存大小。
2、垃圾回收器选择和调优
使用场景:合适的垃圾回收器能够提高应用的响应速度和吞吐量。
3、线程堆栈大小调优
使用场景:合理的线程堆栈大小有助于提高线程创建和管理的效率。
4、调整年轻代和老年代的比例
使用场景:调整年轻代和老年代的比例可以根据应用的特性来优化垃圾回收行为,影响整体的垃圾回收效率。
5、启用GC日志和调试
使用场景:启用GC日志可以帮助你监控垃圾回收过程,并对性能问题进行诊断。
消息队列
1.MQ是什么?有什么作用
MQ是消息队列,先进去的先出去的数据结构,消息由生产者生产,然后进行排队,由消费者进行处理。
作用:
1.异步,作用是提高系统的响应速度和吞吐量
2.解耦,减少服与服的耦合度,减少响应时间,提高性能稳定性和可扩展性
3.削峰,稳定系统对突发流量的处理
应用场景:电商,用户要求响应时间短的类型
2.如何进行MQ产品的选型
MQ选型:ActiveMQ、RocketMQ、RabbitMQ、Kafka对比_rocketmq和rabbitmq哪个用的多-CSDN博客
MQ目前的产品RocketMQ,RabbitMQ,Kafka,ActiveMQ
1.Kafka
优点 吞吐量大,性能大,集群高可用
缺点 会丢失数据,功能单一
使用场景 日志分析 大数据采集等
2.RabbitMQ
优点 消息可靠性高,功能全面
缺点 吞吐量低 消息积累会严重影响性能 使用的是erlang语言(不好定制)
使用场景 小规模的场景
3.RocketMQ
优点 高吞吐 高性能 高可用 功能非常全面
缺点 开源版不如商业版功能 官方文档和周边不够成熟
使用场景 都可以用
3.如何能保证消息不丢失
1.生产者发送消息不丢失
1.消息发送给予反馈
2.手动开启事物保证消息不丢失
3.Publisher ,Confirm 发布确认的方式
1.同步会丢失,异步不会丢失,采用异步的方式
2.消息存盘,保存在磁盘上面,发布一条后,再删除
4.消费者不丢失,采用默认方式消费
5.手动提交
4.如何保证消息消费的幂等性
消息中间件(三)——如何保证消息幂等性_如何保证消息的幂等性-CSDN博客
1.生产者不重复发消息到MQ
mq内部可以为每条消息生成一个全局唯一、与业务无关的消息id,当mq接收到消息时,会先根据该id判断消息是否重复发送,mq再决定是否接收该消息。
2.消费者不重复消费
消费者怎么保证不重复消费的关键在于消费者端做控制,因为MQ不能保证不重复发送消息,所以应该在消费者端控制:即使MQ重复发送了消息,消费者拿到了消息之后,要判断是否已经消费过,如果已经消费,直接丢弃。
5.如何保证消息的顺序
RabbitMQ如何保证消息的顺序性【重点】-CSDN博客
6.如何保证消息队列的高效读写
消息队列-如何保证消息队列的高可用?_消息队列保证高可用-CSDN博客
7.MQ如何保证分布事物的最终一致性
RocketMQ事务消息如何保证数据的最终一致性_rocketmq 一致性-CSDN博客
1.通过 MQ 和补偿机制来保证数据的一致性
2.消费者保证幂等性消费,不然会造成重复消费和多次消费
8.让你去设计MQ你会怎么去设计?
1.实现一个单机队列的数据结构,是高效的,可扩展的
2.把单机队列拓展成可分布式的 ,集群进行处理
3.定制的一个策略,根据主题进行消息定制 (根据主题发送消息策略)I/O ,实现0拷贝
4.实现一些高效的读写
缓存
1.为什么使用缓存
缓存具有高性能, 高可用的特性 ,减少对数据库的直接访问,比如连接次数
2.缓存穿透,击穿,雪崩
面试redis(缓存)--01-CSDN博客
1.缓存穿透(恶意攻击,数据库没有值)
缓存,数据库都查询不到数据
解决方法
1.缓存空值
2.设置访问名单
3.采用布隆过滤器 (空间查询效率高)
4.进行实时监控
2.缓存击穿(数据库中有值)
给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来,这些并发的请求可能会瞬间把DB压垮。
解决方案:
1.设置热点缓存永不过期
2.预设热门数据
3.设置实事监控
4.互斥锁
5.逻辑穿透
3.雪崩
缓存大量过期,导致请求直接访问数据库,导致雪崩
解决方法
1.添加多级缓存
2.设置更新标识缓存
3.将缓存过期时间分散
4.锁,队列的机制