开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情
一、 什么是乐观锁
先来看看官网的解释:
摘百度百科:
乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。而乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号等于数据库表当前版本号,则予以更新,否则认为是过期数据。
二、配置
乐观锁的配置需要两步:
1、配置插件
package com.shang.config;
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@MapperScan("com.shang.mapper")
@EnableTransactionManagement //事务管理
@Configuration
public class MybatisPlusConfig {
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
2、在实体类字段上添加注解
package com.shang.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@TableId(type = IdType.AUTO) //设置id自增(雪花算法)
private Long id;
private String name;
private Integer age;
private String email;
@Version //乐观锁version注解
private Integer version;
}
3、测试一下
先用单线程看一下乐观锁能不能成功修改:
/**
* 测试乐观锁成功
*/
@Test
public void testOptimisticLocker(){
//1 查询用户
User user = userMapper.selectById(1L);
//2 修改用户
user.setName("hahaha");
user.setEmail("1600506991@qq.com");
userMapper.updateById(user);
}
测试乐观锁成功的运行结果:
可以看到,能够成功修改。
再用一个线程去插队,看看乐观锁失败的效果:
/**
* 测试乐观锁失败 多线程下
*/
@Test
public void testOptimisticLocker2(){
//线程1
User user = userMapper.selectById(1L);
user.setName("hahaha111");
user.setEmail("1600506991@qq.com");
//模拟另一个线程插队
User user2 = userMapper.selectById(1L);
user2.setName("hahaha222");
user2.setEmail("1600506991@qq.com");
userMapper.updateById(user2); //插队,抢先更新
//自旋锁来多次尝试提交
userMapper.updateById(user); //如果没有乐观锁,就会覆盖插队线程的值
}
测试一下乐观锁失败的运行结果:
可以看到,在线程2插队以后,原来的user并没有再将插队线程的值覆盖,这就是乐观锁的作用。