为了防止分布式系统中的多个进程之间相互干扰,我们需要一种分布式协调技术来对这些进程进行调度。而这个分布式协调技术的核心就是来实现这个分布式锁。
概览
提示:以下是本篇文章正文内容,下面案例可供参考
一、分布式锁流程
1、定义锁的接口Lock
2、在AbstractLock模板锁里面实现getLock方法,实现通用的逻辑。
3、不能确实的步骤,作为虚拟方法,甩锅给子类实现。
4、子类只需要聚焦自己的小步骤逻辑,实现tryLock,waitLock,unLock方法
二、代码实现
package com.zyc.zk;
public abstract class AbstractLock implements Lock{
public synchronized void getLock() {
//任务通过竞争获取锁才能对该资源进行操作(①竞争锁);
// 当有一个任务在对资源进行更新时(②占有锁),
// 其他任务都不可以对这个资源进行操作(③任务阻塞),
// 直到该任务完成更新(④释放锁)
//尝试获得锁资源
//①竞争锁
if (tryLock()) {
// System.out.println("##获取lock锁的资源####");
} else {
//③任务阻塞
waitLock();
// 重新获取锁资源
getLock();
}
}
// ②占有锁
public abstract boolean tryLock();
// 等待
public abstract void waitLock();
}
MySql实现分布式锁
利用数据库自身提供的锁机制实现,要求数据库支持行级锁(InnoDB存储引擎);
流程:
代码如下(示例):
package com.zyc.mysql;
import com.zyc.mapper.LockMapper;
import com.zyc.zk.AbstractLock;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service("mysqlLock")
public class MysqlLock extends AbstractLock {
@Resource
private LockMapper mapper;
//所有的线程都往数据库插入主键值相同的数据
private static final int LOCK_ID = 1;
//非阻塞式加锁
public boolean tryLock() {
try {
mapper.insert(LOCK_ID);
} catch (Exception e) {
return false;
}
return true;
}
//让当前线程休眠一段时间deer
public void waitLock() {
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void unLock() {
mapper.deleteByPrimaryKey(LOCK_ID);
}
}
编写测试方法:
package com.zyc.test;
import com.zyc.LockApp;
import com.zyc.simple.OrderNumGenerator;
import com.zyc.zk.Lock;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@SpringBootTest(classes = LockApp.class)
@RunWith(SpringRunner.class)
public class MySqlTest {
private OrderNumGenerator orderNumGenerator = new OrderNumGenerator();
// 使用lock锁
// private java.util.concurrent.locks.Lock lock = new ReentrantLock();
@Resource(name = "mysqlLock")
private Lock lock;
@Test
public void testGetOrderNumber() throws InterruptedException {
System.out.println("####生成唯一订单号###");
for (int i = 0; i < 50; i++) {
new Thread(
new Runnable() {
public void run() {
getNumber();
}
}
).start();
}
Thread.currentThread().join();
}
public void getNumber() {
try {
lock.getLock();
String number = orderNumGenerator.getNumber();
System.out.println(Thread.currentThread().getName() + ",生成订单ID:" + number);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unLock();
}
}
}
结果:
MySql实现分布式锁的优缺点
优点:
- 实现简单,稳定可靠
缺点:
- 性能差,无法适应高并发场景;
- 容易出现死锁的情况;
- 无法优雅的实现阻塞式锁;
ZK实现分布式锁
实现思路:
基于zk的节点特性以及watch机制实现
代码实现:
1.连接ZK
//将重复代码写入子类中..
public abstract class ZookeeperAbstractLock extends AbstractLock {
// zk连接地址
private static final String CONNECTSTRING = "127.0.0.1:2181";
// 创建zk连接
protected ZkClient zkClient = new ZkClient(CONNECTSTRING);
protected static final String PATH = "/lock";
protected static final String PATH2 = "/lock2";
}
2.tryLock
//尝试获得锁
public boolean tryLock() {
try {
zkClient.createEphemeral(PATH);
return true;
} catch (Exception e) {
//如果创建失败报出异常
// e.printStackTrace();
return false;
}
}
尝试创建 /lock 节点,如果创建成功获得锁
3.unLock
public void unLock() {
//释放锁
if (zkClient != null) {
zkClient.delete(PATH);
}
}
操作完毕后释放锁
4.waitLock
public void waitLock() {
IZkDataListener izkDataListener = new IZkDataListener() {
public void handleDataDeleted(String path) throws Exception {
// 唤醒被等待的线程
if (countDownLatch != null) {
countDownLatch.countDown();
}
}
public void handleDataChange(String path, Object data) throws Exception {}
};
// 注册事件
zkClient.subscribeDataChanges(PATH, izkDataListener);
//如果节点存
if (zkClient.exists(PATH)) {
countDownLatch = new CountDownLatch(1);
try {
//等待,一直等到接受到事件通知
countDownLatch.await();
} catch (Exception e) {
e.printStackTrace();
}
}
// 删除监听
zkClient.unsubscribeDataChanges(PATH, izkDataListener);
}
使用countdownLatch 当/lock节点被删除再发送通知
ZK实现分布式锁的优缺点
优点:
- 性能好,稳定可靠性高,能较好的实现阻塞式锁
缺点:
- 实现相对复杂