实现分布式锁的步骤:
① 准备两张表(两张表的主键相同,利用mysql的唯一约束实现分布式锁),一张用来存任务,另一张存任务的锁
② 从任务表中获取要执行任务的数据信息
③ 向任务锁表中插入当前任务,若插入成功,则拿到了任务锁,可以执行任务,否则就不能执行任务
创建数据库表
# 创建任务表
create table if not exists `task`(
id bigint primary key auto_increment comment '主键',
description varchar(100) not null comment '任务描述'
);
# 创建任务锁表
create table if not exists `task_lock`(
id bigint primary key comment '主键',
description varchar(100) not null comment '任务描述'
);
# 添加任务
insert into `task`
(id, description)
values
(1,'说句hello world');
Java实体类
任务类
public class Task {
private Long id;
private String description;
/*getter setter ...*/
}
任务锁类
public class TaskLock {
private Long id;
private String description;
/*getter setter ...*/
}
mapper(直接继承的mybatisplus的BaseMapper)
public interface TaskMapper extends BaseMapper<Task> {
}
public interface TaskLockMapper extends BaseMapper<TaskLock> {
}
开启5000个线程模拟5000个服务去抢这一个任务资源
@SpringBootTest
public class LockTest {
@Autowired
private TaskMapper taskMapper;
@Autowired
private TaskLockMapper taskLockMapper;
@Test
void test_distributeLock_mysql() throws InterruptedException {
Runnable run = () -> {
// 获取id为1的任务信息
Task task = taskMapper.selectById(1);
// 向任务锁表中插入数据,若插入成功,代表拿到了锁,可以执行任务
TaskLock taskLock = new TaskLock();
taskLock.setId(task.getId());
taskLock.setDescription(task.getDescription());
try {
taskLockMapper.insert(taskLock);
// 拿到锁,执行打印任务
System.out.println("hello world!");
}catch (Exception e){
// 未拿到锁
}
};
List<Thread>threadPool = new ArrayList<>(5000);
for (int i = 0; i < 5000; i++) {
Thread thread = new Thread(run);
thread.start();
threadPool.add(thread);
}
for (Thread thread : threadPool) {
thread.join();
}
}
}
执行结果:
可以看到5000个线程只有一个线程拿到了任务,执行完这个任务之后,其他线程也不会再次执行这个任务