1、在进行单机应用开发时,设计并发同步的时候,我们采用Synchronized或Lock的方式来解决多线程间的代码同步问题。
2、当我们的应用是分布式集群工作的情况下,在采用synchronized或lock的方式就不行了,此时我们需要一种更加高级的锁机制,来处理这种跨机器的进程之间的数据同步问题--这就是分布式锁
3、分布式锁可以使用三种方式来实现,如下图:
4、本文我们只介绍Zookeeper实现分布式锁,其他两种不做介绍。
5、ZooKeeper分布式锁的原理
核心思想:当客户端要获取锁,则创建节点,使用完锁,则删除该节点
具体步骤如下:
1>、客户端获取锁时,在lock节点下创建临时顺序节点;
2>、然后获取lock下面的所有子节点,客户端获取到所有的子节点后,如果发现自己创建的节点序号最小,那么就认为该客户端获取到了锁。使用完锁后,将该节点删除;
3>、如果发现自己创建的节点并非lock所有子节点中最小的,说明自己还没有获取到锁,此时客户端需要找到比自己小的那个节点,同事对其注册事件监听器,监听删除事件;
4>、如果发现比自己小的那个节点被删除,则客户端的Watcher会收到相应通知,此时再次判断自己创建的节点是否是lock子节点中最小的,如果是则获取到了锁,如果不是则重复以上步骤继续获取到比自己小的一个节点并注册监听。
6、Curator实现分布式锁有种方式
InterProcessSemaphoreMutex:分布式排它锁(非可重入锁)
InterProcessMutex:分布式可重入排它锁
InterProcessReadWriteLock:分布式读写锁
InterProcessMultiLock:将多个锁作为单个实体管理的容器
InterProcessSemaphoreV2:共享信号量。
7、模拟售票过程中使用分布式锁来确保数据正确
需求:模拟携程和飞猪以及去哪了三个客户端分别出售10张票,确保出票不重不出负票
1>、首先虚拟机中开启ZooKeeper服务以及客户端,使用SecureCRT连接虚拟机,然后进入到ZooKeeper安装目录的bin目录下,然后启动服务和客户端
--启动zookeeper服务
./zkServer.sh start
--启动客户端
./zkCli.sh
2>、在idea中编写售票类
public class Ticket12306 implements Runnable {
//要出售的票
private int tickets = 10;
//创建锁
private InterProcessMutex lock;
//初始化锁
public Ticket12306() {
ExponentialBackoffRetry retry = new ExponentialBackoffRetry(3000, 10);
Curatorframework client = CuratorframeworkFactory.builder()
.connectString("192.168.117.128:2181")
.sessionTimeoutMs(60 * 1000)
.connectionTimeoutMs(15 * 1000)
.retryPolicy(retry)
.build();
//启动服务
client.start();
//初始化锁
lock = new InterProcessMutex(client,"/lock");
}
@Override
public void run() {
while(true){
try {
//获取锁
lock.acquire(3, TimeUnit.SECONDS);
//开始售票
if(tickets>0){
System.out.println(Thread.currentThread()+" : "+tickets);
Thread.sleep(1000);
tickets--;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//释放锁
try {
lock.release();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
3>、启动三个线程分别模拟是携程、飞猪、去哪了上应用程序去售票
public static void main(String[] args) {
//创建Ticket12306对象
Ticket12306 ticket = new Ticket12306();
//创建两个线程对象
Thread thread = new Thread(ticket,"飞猪");
Thread thread2 = new Thread(ticket,"携程");
Thread thread3 = new Thread(ticket,"去哪了");
thread.start();
thread2.start();
thread3.start();
}
4>、运行程序,查看售票情况,可以看到售票情况是正常的,没出现多售的情况
5>、查看的客户端节点创建情况
从上图可以看到,在售票时,三个程序分别都创建了临时顺序节点,然后当售卖完后,释放锁,则节点删除。
路漫漫其修远兮,吾将上下而求索,希望此篇文章对大家有所帮助......



