分布式锁

为了防止分布式系统中的多个进程之间相互干扰,我们需要一种分布式协调技术来对这些进程进行调度。而这个分布式协调技术的核心就是来实现这个分布式锁。


概览

在这里插入图片描述


提示:以下是本篇文章正文内容,下面案例可供参考

一、分布式锁流程

在这里插入图片描述
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实现分布式锁的优缺点

优点:

  • 性能好,稳定可靠性高,能较好的实现阻塞式锁

缺点:

  • 实现相对复杂
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值