Day05
1. MySQL脏读和幻读的区别
脏读是指一个事务读取到了另一个事务未提交的数据,比如事务B更新了账户余额还没提交,事务A就读到了这个更新结果,但如果事务B回滚,事务A读到的数据就是“脏数据”。
幻读是指在同一个事务中,两次执行相同的查询语句,结果行数不一致,比如第一次查询5条订单,另一个插入了一条新订单,第二次再查就是6条,这种多出来的数据就叫幻读。
区别:脏读影响的是“值”,幻读影响的是“行”,避免脏读可以用 Read Committed,避免幻读一般用 Repeatable Read 或 Serializable。
2. 如何避免幻读?
- 对于快照读(普通SELECT):InnoDB 使用 MVCC(多版本并发控制)技术,通过技术版本链,在一个事务内部始终读取事务开始时的一致性试图,即使其他事务插入了新数据,当前事务也读不到,自然避免了幻读现象。
- 对于当前读(如SELECT…FOR UPDATE):InnoDB 会使用 Next-Key Lock(记录锁 + 间隙锁)来锁定读取的记录范围,防止其他事务在该范围内插入数据,从而避免了由于并发插入而导致的幻读。
3. Java双亲委派机制是什么?
Java 双亲委派机制是一种类加载的设计模式,用于保证 Java 类加载的安全性和稳定性。
核心思想:当一个类加载器收到类加载请求时,不会自己先去加载这个类,而是先把请求委派给它的父加载器去加载。父加载器再继续委派给自己的父加载器,直到顶层的启动类加载器(Bootstrap ClassLoader)。如果父加载器能够完成加载,就直接返回结果;如果无法加载,子加载器才会尝试自己去加载。
好处:
- 避免重复加载同一个类,保证类的唯一性。
- 防止恶意类覆盖核心 Java 类,提升安全性。
- 保证 Java 核心库的稳定性和兼容性。
4. 垃圾回收cms和g1的区别是什么?
CMS(Concurrent Mark-Sweep)垃圾回收器采用标记-清除算法,主要目标是减少老年代的停顿时间,通过并发标记和清除垃圾来实现低延迟,但清理阶段仍可能产生较长的暂停且容易产生内存碎片。
G1(Garbage-First)则将堆划分为多个独立的小区域,通过并发标记和分区优先回收垃圾较多的区域,能更好地控制停顿时间,适合大堆内存和多核环境,兼顾低延迟和高吞吐,是更现代、更智能的垃圾回收器。
特性 | CMS(Concurrent Mark-Sweep) | G1(Garbage-First) |
---|---|---|
设计目标 | 减少老年代 GC 停顿时间,实现低暂停 | 低暂停、高吞吐,兼顾大堆内存和多核处理 |
回收机制 | 标记-清除(Mark-Sweep),主要针对老年代垃圾回收 | 分区回收(Region-based),对年轻代和老年代统一管理 |
内存划分 | 年轻代和老年代分开管理 | 堆划分成多个大小相同的区域(Region),灵活管理 |
垃圾回收过程 | 并发标记后,清除垃圾(清理阶段不是并发的,可能停顿) | 并发标记后,优先回收回收收益高的区域,分多次回收避免长时间停顿 |
缺点 | 清理阶段可能会产生较长停顿(Stop-the-World),且易产生碎片 | 实现复杂,JDK7及以后版本才有,适合大堆内存 |
适用场景 | 对延迟敏感但堆较小的系统 | 大内存、多核服务器应用,对延迟和吞吐均有较好支持 |
5. Spring三级缓存解决循环依赖问题?
循环依赖指的是多个 Bean 在实例化过程中相互依赖,形成闭环。这种问题会在 Spring 容器场景 Bean 时触发:Spring 在创建 A Bean 时,发现它依赖 B,然后去创建 B,又发现 B 也依赖 A,如果没有处理机制,就会一直递归下去,最终导致栈溢出或创建失败。
三种情况是根据注入方式(构造 VS setter)和作用域(singleton VS prototype)不同产生的:
- 构造器注入 + 循环依赖:Spring 在创建 Bean 时,必须一次性完成构造方法,所以如果 A 和 B 都通过构造方法互相依赖,就无法中断流程,也无法提前暴露任何 Bean,因此无法解决。
- Setter 注入 + prototype 多例:prototype Bean 每次都会新建实例,而且 Spring 不会进行缓存或管理原型 Bean 的生命周期,因此也无法提前暴露或解决循环依赖。
- Setter 注入 + singleton 单例:单例 Bean 是由 Spring 容器创建并容易管理的,Spring 可以提前将创建中的 Bean 暴露出来,后续 Bean 再依赖它时就能获取“早期引用”,从而打破死循环。
Spring 通过三个层级缓存 Bean 的不同状态来解决循环依赖:
- singletonObjects(一级缓存):保存完全初始化好的 Bean。
- earlySingletonObjects(二级缓存):保存实例化但未初始化的“早期 Bean。
- singletonFactories(三级缓存):保存创建早期 Bean 的 ObjectFactory 工厂。
过程举例:
- 创建 A,发现依赖 B → 先把 A 的 ObjectFactory 存入三级缓存。
- 开始创建 B,B 依赖 A → 去三级缓存中拿出 A 的早期引用注入。
- 最终 A 和 B 都完成初始化,从缓存中升级为正式 Bean。
6. 如何使用Spring实现事务?
- 使用注解方式:只需在业务方法上加 @Transactional 注解,Spring 会通过 AOP 动态代理的方法在方法调用前后加上事务管理逻辑。
- 编程式事务:通过手动控制事务提交/回滚。
// 注解方式
@Service
public class OrderService {@Autowiredprivate OrderMapper orderMapper;@Transactional // 开启事务public void createOrder(Order order) {orderMapper.insert(order); // 插入订单// 其他可能会抛异常的操作,比如库存扣减、支付处理等}
}
// 编程式
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;@Service
public class UserService {@Autowiredprivate UserDao userDao;@Autowiredprivate TransactionTemplate transactionTemplate;public void addUser(User user) {transactionTemplate.execute(new TransactionCallbackWithoutResult() {@Overrideprotected void doInTransactionWithoutResult(TransactionStatus status) {userDao.insert(user);// 其他数据库操作}});}
}
7. 介绍事务传播模型有哪些?
事务传播行为是指一个事务方法被另一个事务方法调用时,事务如何传播和处理。Spring 事务传播机制就是解决调用链上多个事务之间的关系和边界问题。
传播行为 | 含义说明 |
---|---|
PROPAGATION_REQUIRED(默认) | 如果当前没有事务,则新建一个事务;如果已有事务,加入该事务。 |
PROPAGATION_SUPPORTS | 如果当前有事务,则加入事务;如果没有事务,则以非事务方式执行。 |
PROPAGATION_MANDATORY | 必须在一个已有事务中运行;如果当前没有事务,则抛出异常。 |
PROPAGATION_REQUIRES_NEW | 始终新建一个新事务,如果当前存在事务,则将当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式运行,如果当前存在事务,则将当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式运行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行(通过 savepoint 实现部分回滚);如果没有事务,则新建事务。 |
8. Springboot常用注解有哪些?
springboot 启动相关
注解 | 作用 |
---|---|
@SpringBootApplication | 组合注解,包含 @Configuration 、@EnableAutoConfiguration 、@ComponentScan ,标志主类为 Spring Boot 启动入口。 |
@ComponentScan | 扫描指定包及其子包下的 Bean,默认扫描启动类所在包及其子包。 |
Bean 定义与注入相关
注解 | 作用 |
---|---|
@Component | 普通组件类,交给 Spring 管理。 |
@Service | 业务逻辑层组件。 |
@Repository | 持久层组件(DAO 层),可支持 Spring 异常转换。 |
@Controller | 表示控制器类,用于处理请求。 |
@RestController | @Controller + @ResponseBody ,用于构建 RESTful API。 |
@Autowired | 自动注入依赖对象,按类型注入。 |
@Qualifier | 与 @Autowired 搭配使用,按名称指定注入 Bean。 |
@Value | 注入配置文件中的值。 |
@Configuration | 标明类是配置类,相当于 XML 中的 <beans> 。 |
@Bean | 将方法的返回值注入为 Spring 容器中的一个 Bean。 |
请求处理相关
注解 | 作用 |
---|---|
@RequestMapping | 通用映射请求路径,可以用于类或方法上。 |
@GetMapping / @PostMapping / @PutMapping / @DeleteMapping | 专门处理 GET、POST 等请求方式的简化注解。 |
@RequestParam | 获取请求参数(如 ?id=1)。 |
@PathVariable | 获取 URL 路径中的变量(如 /user/{id})。 |
@RequestBody | 将请求体中的 JSON 自动封装成对象。 |
@ResponseBody | 方法返回值作为响应体(JSON/XML 等),不是视图名。 |
事务与数据库
注解 | 作用 |
---|---|
@Transactional | 表示方法或类需要事务管理,支持传播机制。 |
Springboot 配置与自动化
注解 | 作用 |
---|---|
@EnableAutoConfiguration | 开启 Spring Boot 的自动配置功能。 |
@ConfigurationProperties | 将配置文件中的一组属性自动注入为 Java Bean。 |
@EnableConfigurationProperties | 配合 @ConfigurationProperties 使用,启用配置绑定。 |
其他使用注解
注解 | 作用 |
---|---|
@Slf4j | 来自 Lombok,自动注入日志对象 log。 |
@Data | Lombok 注解,自动生成 Getter、Setter、toString、构造函数等。 |
@ConditionalOnProperty | 配置类是否生效,取决于配置文件中某个值是否匹配。 |
@Profile | 指定该类或方法在哪个 profile(如 dev/test/prod)下生效。 |
9. 介绍下BIO、NIO、AIO?
BIO(阻塞I/O):是一种同步、阻塞的通信方式,服务器为每个客户端连接分配一个线程,当线程进行读写操作会一直阻塞,直到有数据可读或写入完成。适用于连接数较少的传统应用,简单但不适合高并发。
NIO(非阻塞I/O):是一种同步、非阻塞的方式,引入了 Channel(通道)、Buffer(缓冲区)和 Selector(选择器),一个线程可以处理多个连接,通过事件轮询机制提升并发处理能力。适用于高性能、高并发场景,如 Netty。
AIO(异步I/O):是一种异步、非阻塞的通信方式,I/O 操作由操作系统完成后通知应用程序(回调机制),开发者无需关心轮询或阻塞问题。适合对性能要求极高的系统,如金融、高并发服务器等。