Synchronized (Java 关键字)
作用: 能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果。
两个用法
对象锁
- 方法锁:默认锁对象为 this 当前实例对象
- 同步代码块锁:自己指定锁对象
类锁
- synchronized 修饰 静态 的方法
- synchronized 指定锁为Class对象
多线程访问同步方法的 7 种情况(面试常考)
- 两个线程同时访问 一个对象 的同步方法
- 两个线程访问的是 两个对象 的同步方法
- 两个线程访问的是 synchronized 的 静态 方法
- 同时访问 同步 方法与 非同步 方法
- 访问同一个对象的 不同的 普通同步方法
- 同时访问 静态 synchronized 和 非静态 synchronized 方法
- 方法 抛异常 后,会 释放锁
7 种情况总结:3 点核心思想
- 一个锁只能同时被一个线程获取,没有拿到锁的线程必须等待(对应 1、 5 中情况);
- 每个实例都对应有自己的一把锁,不同实例之间互不影响;例外:锁对象是 *.class 以及 synchronized 修饰的是 static 方式的时候,所有对象共用一把 类锁 (对应 2、 3、 4、 6 种情况);
- 无论是方法正常执行完毕或者方法抛出异常,虚拟机都会帮助我们自动释放锁(对应第 7 种情况)
Synchronized 缺陷
对比 Lock 接口、读写锁等
- 效率低:锁的释放情况少、试图获得锁时不能设定超时、不能中断一个正在试图获得锁的线程
- 不够灵活(读写锁更灵活):加锁和释放的时机单一,每个锁仅有单一的条件(某个对象),可能是不够的
- 无法知道是否成功获取到锁
Synchronized 常见面试问题
-
使用注意点:
- 锁对象不能为空
- 作用域不宜过大,提高效率
- 避免死锁,比如遇到 A 持锁1要 锁2 的同时 B 持锁2 要锁1
-
如何选择 Lock 和 Synchronized 关键字?
- 有现成的工具包,就用现成的
- 非要用,优先选用 Synchronized ,减少代码量
Redis
Redis 在 Java Web 中的主要应用场景
- 存储 缓存 用的数据
- 需要高速读/写的场合 使用它快速读/写
- 数量控制器
- 消息队列
消息队列的概念与应用
应用场景
- 冗余:比如数据排队处理并持久化后,删除原数据
- 解耦:分离两套系统
- 流量消峰:秒杀抢购
- 异步通信:队列使请求直接返回,后面慢慢处理
- 扩展性:
- 排序保证:
队列介质
- MySQL:可靠性高、易于实现、速度慢
- Redis:速度快,单条大消息包时候效率低
- 消息系统(RabbitMQ等):专业性强、可靠、学习成本高
消息处理触发机制
- 死循环方式读取:易实现,故障时无法及时恢复
- 定时任务:压力均分,有处理量上限
- 守护进程:类似于 PHP-FPM 和 PHP-GG,需要 Shell 基础
数据库设计
ER 图例说明
- 矩形:表示实体集,矩形内写实体集的名字
- 菱形:表示联系集
- 椭圆:表示实体的属性
- 线段:将属性连接到实体集,或将实体集连接到联系集
设计范式
-
第一范式
定义: 数据库表中的所有字段都是单一属性,不可再分,这个单一属性是由基本的数据类型所构成的
换句话说 ,第一范式要求数据库中的表都是二维表
-
第二范式
定义: 数据库的表中不存在非关键字段对任一候选关键字段的部分函数依赖
部分函数依赖 是指存在着组合关键字中的某一关键字决定非关键字的情况
换句话说: 所有单关键字段的表都符合第二范式
-
第三范式
第三范式是在第二范式的基础之上定义的,
如果数据表中不存在非关键字段,
对任意候选关键字段的传递函数依赖,
则符合第三范式
-
BC 范式(Boyce.Codd 范式)
定义: 在第三范式的基础之上,数据库表中如果不存在任何字段对任一候选关键字段的传递函数依赖,则符合 BC 范式
也就是说,如果是复合关键字,则复合关键字之间也不能存在函数依赖关系
物理设计
- 选择合适的数据管理系统(Orale、MySQL、PgSQL)
- 定义数据库、表及字段的命名规范
- 根据所选的 DBMS 系统选择合适的字段类型(char、vachar)
- 反范式化设计(如:出于读写效率考量,增加数据冗余)
MySQL 常见的存储引擎
存储引擎 | 事务 | 锁粒度 | 主要应用 | 忌用 |
---|---|---|---|---|
MyISAM | 不支持 | 支持并发插入的表级锁 | select,insert | 读写操作频繁 |
MRG_MyISAM | 不支持 | 支持并发插入的表级锁 | 分段归档,数据仓库 | 全局查找过多的场景 |
Innodb | 支持 | 支持 MVCC 的行级锁 | 事务处理 | 无 |
Archive | 不支持 | 行级锁 | 日志记录,只支持insert,select | 需要随机读取,更新,删除 |
Ndb cluster | 支持 | 行级锁 | 高可用性 | 大部分应用 |
操作系统
进程与线程
区别与联系
- 进程是系统资源分配的最小单位
- 线程是程序执行的最小单位
- 进程使用独立的数据空间
- 线程共享同一线程的数据空间
线程调度
- 时间片轮转调度
- 先来先服务调度
- 优先级调度
- 多级反馈队列调度
- 高响应比优先调度
网络知识
TCP协议
- 建立连接:三次握手
- 管理连接:四次挥手
- 报文状态标志与连接状态
- Nagel 算法与 ACK 延迟
- KeepALive - 是在长时间没有数据发送的情况下保持连接可用的机制,需要了解开启和设置方式
- 滑动窗口与流量控制
HTTP 协议
协议
- Method
- Header
- Cookies
UrlEncode
状态码
HTTPS
HTTP2
- 多路复用
- Stream
- 流量控制
- 服务端推送
- 头部压缩
UDP
- 非连接
- 非可靠传输
- 效率高
QUIC
- 避免前序包阻塞(HOL阻塞)
- 零 RTT 建连
- FEC 前向纠错
OSI 模型
-
应用层
-
表示层
-
会话层
-
传输层
-
网络层
-
数据链路层
-
物理层
(物联网输会示用)
TCP/IP 模型
1. 应用层
对应 OSI 的应用层、表示层、会话层
- Telnet 协议能提供远程登录服务
- FTP(File Transfer Protocol)文件传输协议
- SMTP(Simple Mail Transaction Protocol)简单邮件传输协议
- SNMP(Simple Network Management Protocol)简单网络管理协议
- HTTP(HyperText Transfer Protocol)超文本传输协议
- RPC(Remote Procedure Call)远程过程调用
2. 传输层
- TCP(Transmission Control Protocol)传输控制协议
- UDP(User Datagram Protocol)用户数据包协议
- QUIC(Quick UDP Internet Connection)快速 UDP 网络连接 - 基于 UDP 实现原 HTTP 功能,现已被标准化为 HTTP3 协议
3. 网络层
- IP(Internet Protocol)网络协议
- ICMP(Internet Control Message Protocol)网络控制信息协议
- IGMP(Internet Group Management Protocol)网络组群管理协议
4. 网络接口层
对应 OSI 的数据链路层、物理层
- ARP(Address Resolution Protocol)地址解析协议
- RARP(Reverse Address Resolution Protocol)反向地址转换协议
设计模式
创建型
工厂方法模式(常用)
在实际业务中经常用到,也是面试的主要考察点,是创建不同类型实例常用的方式
- spring 中的 bean 都是有不同工厂类创建的
抽象工程模式
单例模式(常用)
线程安全实现的常用三种方法
- 静态初始化(饿汉),不管是否使用都会创建
- 双检锁(懒汉),单例变量必须要用
volatile
修饰 - 单例注册表,spring 中 bean 的单例模式就是用该方法实现
建造者模式
适用于一个对象拥有很多复杂的属性,需要根据不同情况创建不同的具体对象
- 创建 Protocol Buffer 对象时,需要用到 Builder
原型模式
结构型
适配器模式(常用)
类似于转接头,将两种不匹配的对象进行适配,也可以起到对两个不同的对象进行解耦的作用
- SLF4J 可使项目 Log4、Logback 等具体日志实现框架进行解耦,其通过不同适配器与不同框架进行适配,完成日志功能的使用
装饰器模式
代理模式(常用)
在不适合或不能直接引用另一个对象的场景,可以用代理模式对被代理的队形进行访问行为的控制,Java的代理模式分为静态代理和动态代理,静态代理是指在编译时就创建好的代理类,例如在源代码中编写的类,动态代理指在 JVM 运行过程中动态创建的代理类, 如 JDK 动态代理,CDLIB,Javaasist 等
- 例如:在 MyBatis 中 getMapper 时会通知 MapperProxyFactory 及配置文件动态生成的 Mapper 代理对象,代理对象会拦截 Mapper 接口的方法调用,创建对应方法的 MapperMethod 类并执行 execute 方法,然后返回结果
外观模式
桥接模式
组合模式
享元模式
行为型
策略模式
模板方法模式
观察者模式(常用)
也可称为发布订阅模式,适用于一个对象某个行为需要出发一系列操作的场景
- GRPC 中 Stream 流式请求的处理
迭代器模式
责任链模式(常用)
类似工厂流水线,其中的每个节点完成对对象的某一种处理
- Netty 框架的处理消息的 Pipeline 就是采用的责任链模式
命令模式
备忘录模式
状态模式
访问者模式
中介者模式
解释器模式
Java 语言
Java 语言特性
集合类
主要掌握如何实现
动态代理与反射
是 Java 语言的特色,需要掌握动态代理与反射的使用场景
- ORM 框架中会大量使用代理类,RPC 调用时使用反射机制调用实现类的方法
数据类型
也是面试的常见问题,如每种数据类型占用多大空间,数据类型的自动转换与强制转换,基础数据类型与 Wrapper 数据类型的自动装箱与拆箱等
对象引用
(可自行搜索)
Java基础常考点 - Map
HashMap
-
通过数组加链表实现
数组中的元素为一个链表,通过计算存入对象的 hashcode,确认存入位置,用链表解决散列冲突,链表的节点存入的是键值对
-
填充因子的作用
-
Map 扩容的 rehash 机制
-
容量是二的幂次方
是为了方便按位与操作计算余数,比求模更快
-
多线程风险的原因
对线程 put 时,会在超过填充因子的情况下 rehash.HashMap 为避免尾部遍历,链表插入采用头插法,多线程场景下可能产生死循环
ConcurrentHashMap
-
分段锁思想
1.7 中采用 segment 分段加锁,降低并发锁定程度
-
CAS 自旋锁
1.8 中采用 CAS 自旋锁(一种乐观锁实现模式)提高性能,但在并发度较高时,性能一般
-
红黑树
1.8 引入红黑树解决 hash 冲突时的链表查找问题,在链表长度大于 8 且总容量大于 64 时启用,扩容后链表长度小于 6 时重新转为一般链表(8,6,64为默认参数)
Java 版本特性
1.8
- Lambda 表达式
- StreamAPI
- 方法引用
- 接口默认方法
- Metaspace 替换 PremGen
1.9 - 1.10
- 模块系统
- 默认 G1 回收器
- 接口私有方法
- 局部变量判断
- Graal 编译器
1.11
- ZGC
- 字符串 API 增强
- 内建 HTTP Client
面试考察点
基本概念和基本原理
- 理解正确清晰
- 网络协议 4/7 层模型的概念
- TCP 协议流量控制的实现原理
- ……
实现方法和使用方法
- HashMap 在 JDK 1.8 中的实现方式
- 单例模式有哪几种实现方式,什么场景该使用静态方法实现,什么场景该使用双检锁实现
- ……
经常用到的知识点
- 常用的 Linux 命令有哪些,用来解决什么样的问题
- ……
实际应用中容易犯错的点
- == 与 equals 区别是什么
- 对象强引用使用不当会导致内存泄露,考察不同引用方式和作用的理解
- ……
与面试方向相关的知识点
- 中间件、存储、网络相关的考察
- ……
加分项
-
知识点与典型的业务场景关联
如:谈到设计模式时,可以讲 XX 框架在解决 XX 问题时使用了那种设计模式
-
以反例来描述实际场景中误用的危害
如:大量使用反射会影响性能
-
与知识点相关的优化点
如:讲到 TCP 建连和断连时,如遇到洪水攻击或大量 TIME_WAIT 时,可以调整系统参数预防
-
与知识点相关的最新技术趋势
- 如:讲到 ConcurrentHashMap,可以介绍 1.8 的改进细节
- 或:讲到 HTTP 时,能说出 HTTP2 和 QUIC 的特点和实现
-
在了解的前提下,尽量增加回答内容的深度
如:讲到 TCP 的滑动窗口时,能讲到流量与拥塞控制,进一步能指出解决拥塞的不同算法
真题汇总
1. 进程和线程的区别和联系
从资源占用,切换效率,通信方式等方面回答
2. 简单介绍一下进程的切换过程
线程上下文的切换代价,要回答,切换会保存寄存器、栈等线程相关的现场,需要由用户态切换到内核态,可以用 vmstat 命令查看线程上下文的切换情况
3. 你经常使用哪些 Linux 命令,主要用来解决哪些问题
4. 为什么 TCP 建连需要 3 次握手,而断连需要 4 次
5. 为什么 TCP 关闭连接时需要 TIME_WAIT 状态,为什么要等 2MSL
6. 一次完整的 HTTP 请求过程是怎样的
DNS 解析,TCP 建连,HTTP 请求,HTTP 响应等