synchronized是jvm级别的线程同步,当项目使用分布式、集群,就需要使用分布式锁
- 引入依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.9.0</version>
</dependency>
- 添加一个redisson的配置文件
#更多配置详情查看 https://github.com/redisson/redisson/wiki/2.-Configuration
singleServerConfig:
idleConnectionTimeout: 10000
pingTimeout: 1000
connectTimeout: 10000
timeout: 3000
retryAttempts: 3
retryInterval: 1500
reconnectionTimeout: 3000
failedAttempts: 3
#密码
password:
subscriptionsPerConnection: 5
clientName: null
#redis地址
address: "redis://localhost:6379"
subscriptionConnectionMinimumIdleSize: 1
subscriptionConnectionPoolSize: 50
connectionMinimumIdleSize: 32
connectionPoolSize: 64
#索引库
database: 0
#不注释会注入bean失败
# dnsMonitoring: false
# dnsMonitoringInterval: 5000
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
transportMode: NIO
- 修改application.yml
spring:
redis:
host: localhost
port: 6379
database: 0
password:
timeout: 3000ms
redisson:
#指定redisson配置文件的位置
config: classpath:redisson.yml
#使用数据库进行测试
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/admin?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: root
- service
@Service
public class UserService {
@Autowired
private SysUserMapper userMapper;
@Autowired
private RedissonClient redissonClient;
public String addUser(SysUser user){
// 获取锁,以username为锁名称。这样用户名相同的数据添加时,无法并行处理。
// 第二次的请求需要等到第一次请求的锁释放后才可以继续执行
RLock lock = redissonClient.getLock(user.getUsername());
// 默认锁30秒,如果当前线程处理时间过长。
// redisson会在锁时间过了三分之二的时候将锁的时间重新设置为30秒
lock.lock();
//将需要锁住的代码try起来,并在finally中释放锁
try {
// 判断用户账号是否重复
SysUser sysUser = userMapper.selectByUsername(user.getUsername());
// 如果用户存在,抛出异常
if (sysUser != null) {
throw new RuntimeException("用户已经存在");
}
// 模拟线程不安全的情况
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 不重复则添加用户
userMapper.insert(user);
}finally {
// 释放锁
lock.unlock();
}
return "success";
}
}
- controller
@RestController
public class UserController {
@Autowired
private UserService userService;
@PostMapping("addUser")
public String addUser(@RequestBody SysUser user){
return userService.addUser(user);
}
}
-
测试
开启两个不同端口的服务,使用nginx配置集群,轮询访问。
当然分布式锁在一个应用中也是可以使用的,测试也可以只启动一个服务,请求两次。
也可以将锁去除测试一下未上锁的情况。
作者公众号