问题:当服务部署到了集群中,我们对共享资源加锁使用synchronized/Lock已经无能为力了,所以需要引入分布式锁。
针对分布式锁的实现常见的有3种实现:
1.基于数据库,如Mysql、Oracle
2.基于缓存,如redis
3.基于zookeeper
我们本篇讨论的是基于数据库实现。
使用数据库实现分布式锁需要建一张lock表,表中设置一个unique索引,获取锁时,尝试给表中insert记录,若失败,则说明锁被别的线程抢占了,还未释放。当处理完业务,释放锁,删除表中的那条记录即可。
step1:建表
CREATE TABLE `my_lock` ( `id` varchar(32) NOT NULL, `resource` varchar(45) NOT NULL, `createTime` bigint(13) DEFAULT NULL, PRIMARY KEY (`id`,`resource`), UNIQUE KEY `resource_UNIQUE` (`resource`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
tips:表名切勿使用lock,使用关键字语法会报错。
Lock实体:
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("my_lock")
public class Lock {
private String id;
private String resource;
private long createTime;
}
service层和repositroy层代码略
step2:模板锁
使用模板模式写一个锁的抽象类,getLock里在获取锁失败后,会wait一段时间,然后递归地再去获取锁,直到获取到锁为止。
AbstractLock.java
public abstract class AbstractLock {
public void getLock(String lockResource){
if(tryLock(lockResource)){
System.out.println("获得了锁");
}else{
waitLock();
getLock(lockResource);
}
}
public abstract boolean tryLock(String lockResource);
public abstract void waitLock();
public abstract void unLock(String lockResource);
}
step3:锁的实现
MySqlLock.java
@Service
public class MysqlLock extends AbstractLock {
@Autowired
private ILockService iLockService;
@Override
public boolean tryLock(String lockResource) {
boolean flag = true;
try {
Lock lock = new Lock();
lock.setId(IdUtil.simpleUUID());
lock.setResource(lockResource);
lock.setCreateTime(System.currentTimeMillis());
iLockService.insert(lock);
}catch (Exception e){
e.printStackTrace();
flag = false;
}
return flag;
}
@Override
public void waitLock() {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void unLock(String lockResource) {
iLockService.deleteByUniqueKey(lockResource);
}
}
step4:锁的使用
@Autowired
private MysqlLock lock;
@Test
void testDatabaseLock() {
try{
lock.getLock("order");
}catch (Exception e){
}finally {
lock.unLock("order");
}
}



