多个事务间发生冲突解决方法
悲观锁:在sql语句后+for update
为保证数据的一致性和完整性,每次读写数据时都会先加锁,这样可以避免其他事务进行读写的并发操作
通常使用synchronized来实现
乐观锁:在where中判断版本号是否一致
提交数据更改时检查是否有其他事务修改这条数据,若没有,就提交更改,否则回滚事务(CAS)
认为不太可能出现冲突
CAS:无锁状态下,多个线程对一个值的更新
之前:上锁后其他线程不能访问,为提高效率,判断需要修改的值是否被其他线程修改更新过(ABA问题,向需要改变的值加一个版本号)
遇到的问题+例子
当有多个请求同时访问时,出现了多个线程同时访问的情况
解决方案:@Transactional注解
1、为方法添加事务
2、可指定隔离级别避免出现脏读、幻读、不可重复读
3、可指定传播机制
4、指定是否可读等
隔离级别
数据库的事务隔离
脏读: 表示一个事务能够读取另一个事务中还未提交的数据
不可重复读:指的是在一个事务内,事务 A 多次读取同一数据,但事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。
幻读: 比如说一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。
- ISOLATION_READ_UNCOMMITTED:这是事务最低的隔离级别,允许另一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读、不可重复读和幻读。
- ISOLATION_READ_COMMITTED:保证一个事务修改的数据提交后才能被另一个事务读取。另一个事务不能读取该事务未提交的数据。
- ISOLATION_REPEATABLE_READ:这种事务隔离级别可以防止脏读、不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
- ISOLATION_SERIALIZABLE:这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读、不可重复读外,还避免了幻读。(性能低)
默认情况下,@Transactional使用的隔离级别与具体的数据库相关。例如,MySQL的默认隔离级别是REPEATABLE READ,而Oracle的默认隔离级别是READ COMMITTED。
传播行为(Propagation)
- REQUIRED:默认传播行为,当前方法在现有事务中执行,如无事务则创建新事务。
- REQUIRES_NEW:当前方法必须在一个新的事务中执行,暂停现有事务。
- MANDATORY:必须在现有事务中执行,如无事务则抛出异常。
- SUPPORTS:当前方法支持事务,但无事务时非事务执行。
- NOT_SUPPORTED:当前方法非事务方式执行,如有事务则挂起。
- NEVER:当前方法必须在非事务中执行,如有事务则抛出异常。
- NESTED:在现有事务中执行,如无事务则创建新事务,并允许嵌套提交和回滚。
回滚规则(Rollback Rules)
回滚规则定义了在何种情况下事务需要回滚。@Transactional注解默认对运行时异常(如 RuntimeException 和 Error)进行回滚,而不回滚受检异常(如 Exception)。
- rollbackFor:指定哪些异常需要回滚。
- noRollbackFor:指定哪些异常不需要回滚。
synchronized关键字
当两个并发线程(thread1和thread2)访问同一个对象中的代码块时,在同一时刻只能有一个线程得到执行,另一个线程受阻塞,必须等待当前线程执行完这个代码块以后才能执行该代码块。Thread1和thread2是互斥的,因为在执行synchronized代码块时会锁定当前的对象,只有执行完该代码块才能释放该对象锁,下一个线程才能执行并锁定该对象
使用方法:
1.synchronized(this){}包含需要加锁的代码块
2.synchronized加在public的方法上
缺点:线程排队等待,运行时间较长
最后,欢迎大家补充其他见解~