java分布式实现原理_分布式锁原理与实现

场景描述:

小型电商网站,下单,生产有一定业务含义的唯一订单编号。

思路分析:

如果单台服务器已无法撑起并发量,怎么办?集群?

313a21952eb38f7e77ed292b6c198412.png

分布式锁的用途:

3fe2ad49fac3c4d5e630fbfd21b5110e.png

在分布式环境下协同共享资源的使用。

分布式锁的特点:

1.排他性: 只有一个线程能获取到。

2.阻塞性: 其他未抢到的线程阻塞,直到锁释放出来,在抢。

3.可重入性:线程获得锁后,后续是否可重复获取该锁。

我们掌握的计算机技术中,有哪些能提供排他性?

1. 文件系统

2. 数据库: 主键 唯一约束 for update

3. 缓存redis: setnx

4. zookeeper: 类似文件系统

常用的分布式锁实现技术

1. 基于数据库实现分布式锁

性能较差。容易出现单点故障。

锁没有失效时间,容易死锁。

2. 基于缓存实现分布式锁

实现复杂

存在死锁

3. 基于zookeeper实现分布式锁

实现相对简单

可靠性高

性能较好

基于zookeeper实现分布式锁

方式一:

1.去获取锁创建节点

2.获取锁成功,执行业务并且释放锁,等待唤醒。

3. 获取锁失败,注册节点的watcher,阻塞等待,直到上一个成功获取锁释放到锁,才会取消watcher,尝试抢锁。

特性:

同父的子节点不可重名

假如部署在规模较大集群会发生'惊群效应'

1.巨大的服务器性能损耗

2.网络冲击

3.可能造成宕机

方式二: (解决方案)

1.创建一个锁目录lock

2.希望获得锁的线程A就在lock目录下,创建临时顺序节点

3.获取锁目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁

4.线程B获取所有节点,判断自己不是最小节点,设置监听(watcher)比自己次小的节点(只关注比自己次小的节点是为了防止发生“羊群效应”)

5.线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是最小的节点,获得锁。

特性:

临时顺序节点

代码实现

/*** zookeeper锁实现(临时顺序结点)

*@authorskymr

**/

public class ZookeeperLock1 implementsLock, Watcher{public ZookeeperLock1(String url, intsessionTimeOut, String path){this.parrentPath =path;try{//url是zookepper服务器的地址

zk = new ZooKeeper(url, sessionTimeOut, this);

latch.await();

}catch(Exception e) {

e.printStackTrace();

}

}//zk客户端

privateZooKeeper zk;//结点路径

privateString parrentPath;//用于初始化zk的,zk连接是异步的,但连接成功后才能进行调用

private CountDownLatch latch = new CountDownLatch(1);private static ThreadLocal currentNodePath = new ThreadLocal();public voidlock() {if(!tryLock()){

String mypath=currentNodePath.get();//如果尝试加锁失败,则进入等待

synchronized(mypath){

System.out.println(Thread.currentThread().getName()+" lock失败,进入等待");try{

mypath.wait();

}catch(Exception e) {

}

System.out.println(Thread.currentThread().getName()+" lock等待完成");

}//等待别人释放锁后,自己再去加锁

lock();

}else{

System.out.println(Thread.currentThread().getName()+" lock成功");

}

}public void lockInterruptibly() throwsInterruptedException {

}public booleantryLock() {try{//加锁代码是创建一个节点

String mypath =currentNodePath.get();if(mypath == null){

mypath= zk.create(parrentPath + "/", "111".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);

currentNodePath.set(mypath);

}final String currentPath =mypath;

List allNodes = zk.getChildren(parrentPath, false);

Collections.sort(allNodes);//不抛异常就表示创建成功啦

String nodeName = mypath.substring((parrentPath + "/").length());if(allNodes.get(0).equals(nodeName)){//当前结点是最小的节点,获取锁成功

return true;

}else{//监听最小的结点

String targetNodeName = parrentPath + "/" + allNodes.get(0);

System.out.println(Thread.currentThread().getName()+" 需要等待节点删除" +targetNodeName);

zk.exists(targetNodeName,newWatcher() {public voidprocess(WatchedEvent event) {if(event.getType() ==EventType.NodeDeleted){synchronized(currentPath){

currentPath.notify();

}

System.out.println(Thread.currentThread().getName()+" 通知Lock等待中的线程重试加锁");

}

}

});

}return false;

}catch(Exception e) {return false;

}

}public boolean tryLock(long time, TimeUnit unit) throwsInterruptedException {return false;

}public voidunlock() {try{//释放锁,删除节点

String mypath =currentNodePath.get();if(mypath != null){

System.out.println(Thread.currentThread().getName()+" 释放锁");

zk.delete(mypath,-1);

currentNodePath.remove();

}

}catch(Exception e) {

}

}publicCondition newCondition() {return null;

}public voidprocess(WatchedEvent event) {

System.out.println(event);if(event.getType() ==EventType.None){//当连接上了服务器后,初始化完成

latch.countDown();

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值