如何使用zookeeper来完成非公平锁的模拟,在这之前,我们先来,了解下公平锁和非公平锁的区别。
JAVA JDK提供了公平锁,与非公平锁,但这种实现是基于同一个JVM来说的,
如果同一台机器上,不同的JVM,则可以使用文件锁,来实现,但是这些并不是分布式的模式,虽然可以通过RMI的方式来实现,
但比较繁琐。在分布式的场景里,我们可以轻松的使用zookeeper来实现公平锁与非公平锁
基于zookeeper实现的公平锁与非公平锁的区别
先来通俗的看下二者的区别
公平锁,即先来者先得,只有一个厕所的卫生间,想进去只能是按排队顺序来的,比较公平,first挂掉或释放后,会由secend得到锁,依次类推。
非公平锁,比较暴力,只有一个厕所的卫生间,不用排队,外面围了一堆人等着上厕所,当里面的人出来时,外面的人谁强势,而且力气大,谁就能进去,
极端情况下,如果两个人一样力气大,这时候就该厕所门发挥作用了,一次只能挤进去一个人,反映到我们的程序中,这时候就需要代码同步了,保证
任何时候,只有一个人可以拿到锁。
二者的相同点,都保证了,任何情况下,都只能一个人得到某种资源。但实现的方式不同。
实现简述:分布式非公平锁的创建,除了得到锁外,其他的多个监听器,监听同一个锁的情况
实现的流程步骤如下:
序号 | 介绍 | 1 | 创建一个持久znode | 2 | 多个程序并发的去zk服务上,创建同一个短暂无时序性的节点路径,当一个程序,得到锁时,其他程序,只能监听,不能再次创建,创建时需要同步策略 | 3 | 同一时刻只能有一个创建成功者,能得到锁 | 4 | 没成功者,统一监视得到锁的节点 | 5 | 如果中间得到锁的节点,释放了,或者出意外挂掉了,则重复步骤1,2,3,4 |
拓扑图如下:
代码如下:
- /***
- * @author qin dong liang
- *
- * */
- public class LockUnFair3 implements Watcher {
- /**
- * ZK实例
- * */
- private ZooKeeper zk;
- /**原子计数锁,防止在zk没有连上前,执行CURD操作*/
- private CountDownLatch down=new CountDownLatch(1);
- public LockUnFair3() {
- // TODO Auto-generated constructor stub
- }
- public LockUnFair3(String host)throws Exception {
- this.zk=new ZooKeeper(host, 5000 , new Watcher() {
- @Override
- public void process(WatchedEvent event) {
- // TODO Auto-generated method stub
- /**链接上zk服务,岂可取消阻塞计数**/
- if(event.getState()==KeeperState.SyncConnected){
- down.countDown();
- }
- }
- });
- }
- /**
- * 字符编码
- *
- * **/
- private static final Charset CHARSET=StandardCharsets.UTF_8;
- @Override
- public void process(WatchedEvent event) {
- // TODO Auto-generated method stub
- if(event.getType()==Event.EventType.NodeDeleted){
- //如果发现,监听的节点,挂掉了,那么就重新,进行监听
- try{
- // System.out.println("注意有锁退出或释放,公平锁开始抢占........");
- System.out.println("3我可以去抢占了");
- createTemp();
- // check();
- }catch(Exception e){
- e.printStackTrace();
- }
- }
- }
- SimpleDateFormat f=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- /**
- * 关闭zk连接
- *
- * **/
- public void close()throws Exception{
- zk.close();
- }
- Random random=new Random();
- /***
- * 创建锁node,注意是抢占 的
- *
- *
- * */
- public void createTemp()throws Exception{
- Thread.sleep(random.nextInt(2500));//加个线程休眠,实现模拟同步功能
- if(zk.exists("/a/b", this) != null){
- System.out.println("锁被占用,监听进行中......");
- }else{
- String data=zk.create("/a/b", "a".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
- System.out.println("Lock3创建锁成功,节点路径: "+data);
- }
- // System.out.println("2"+data);
- }
- public static void main(String[] args)throws Exception {
- //Slave s=new Slave("192.168.120.128:2181");
- LockUnFair3 lock=new LockUnFair3("192.168.120.128:2181");
- // lock.createPersist();//创建主节点
- lock.createTemp();
- // lock.check();
- Thread.sleep(Long.MAX_VALUE);
- lock.close();
- }
- }
/*** * @author qin dong liang * * */ public class LockUnFair3 implements Watcher { /** * ZK实例 * */ private ZooKeeper zk; /**原子计数锁,防止在zk没有连上前,执行CURD操作*/ private CountDownLatch down=new CountDownLatch(1); public LockUnFair3() { // TODO Auto-generated constructor stub } public LockUnFair3(String host)throws Exception { this.zk=new ZooKeeper(host, 5000 , new Watcher() { @Override public void process(WatchedEvent event) { // TODO Auto-generated method stub /**链接上zk服务,岂可取消阻塞计数**/ if(event.getState()==KeeperState.SyncConnected){ down.countDown(); } } }); } /** * 字符编码 * * **/ private static final Charset CHARSET=StandardCharsets.UTF_8; @Override public void process(WatchedEvent event) { // TODO Auto-generated method stub if(event.getType()==Event.EventType.NodeDeleted){ //如果发现,监听的节点,挂掉了,那么就重新,进行监听 try{ // System.out.println("注意有锁退出或释放,公平锁开始抢占........"); System.out.println("3我可以去抢占了"); createTemp(); // check(); }catch(Exception e){ e.printStackTrace(); } } } SimpleDateFormat f=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); /** * 关闭zk连接 * * **/ public void close()throws Exception{ zk.close(); } Random random=new Random(); /*** * 创建锁node,注意是抢占 的 * * * */ public void createTemp()throws Exception{ Thread.sleep(random.nextInt(2500));//加个线程休眠,实现模拟同步功能 if(zk.exists("/a/b", this) != null){ System.out.println("锁被占用,监听进行中......"); }else{ String data=zk.create("/a/b", "a".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); System.out.println("Lock3创建锁成功,节点路径: "+data); } // System.out.println("2"+data); } public static void main(String[] args)throws Exception { //Slave s=new Slave("192.168.120.128:2181"); LockUnFair3 lock=new LockUnFair3("192.168.120.128:2181"); // lock.createPersist();//创建主节点 lock.createTemp(); // lock.check(); Thread.sleep(Long.MAX_VALUE); lock.close(); } }
以上是实现的代码,需要注意的是,在最后抢占锁时,可能会一下多个节点同时去建立名字一样的节点,由于zookeeper的特点,只能由一个建立成功,其他的会抛出异常,为了避免这种情况,散仙,目前的想到的是,在创建一个节点时,通过线程随机休眠,来达到一个同步情况,但这扔有极端情况,虽然几率很小,就是分布式环境下可能有多个节点随机休眠的时间是一样的,所以第二种做法,可以在zk节点维持一个有序的分布式队列,每次只能第一个得到锁,其他的继续等待,下一次的抢占,如此一来,就能保证任何时刻只有一个节点得到锁。
相关推荐
它的数据模型——ZNode(节点),可以模拟锁的概念,通过创建、删除和更新ZNode来实现锁的获取和释放。 实现分布式全局锁的基本步骤如下: 1. **创建锁节点**:客户端在Zookeeper上创建临时顺序节点,表示请求锁。...
这里使用一个自增操作来模拟锁: ```java @Service public class DistributedLockService { @Autowired private RedisTemplate, String> redisTemplate; public boolean lock(String key) { Long result = ...
8. **分布式锁的其他实现**:除了Redis,还可以利用ZooKeeper、MongoDB等其他数据库或服务来实现分布式锁,但每种方式都有其优缺点,需要根据实际场景选择合适的方法。 以上就是基于SpringBoot和Redis实现分布式锁...
- **公平性**:Redis本身并不支持公平锁,但可以通过客户端策略(如队列等待机制)来模拟实现。 ##### Redis分布式锁示例 假设我们有一个场景需要控制并发访问的次数,可以采用以下步骤: 1. **客户端A尝试获取锁...
这种活动通常吸引大量用户参与,因此,为了提高抢购成功率,开发了各种自动化工具,比如“一键自动完成商品秒杀”的程序。这个压缩包文件包含了实现这一功能的相关代码和资源。 首先,我们要理解秒杀系统的工作原理...
同时,使用乐观锁或者悲观锁来解决并发更新问题。 4. **缓存技术**:为了减轻数据库压力,秒杀项目通常会使用缓存,如Redis,来存储热门商品信息,实现快速响应。但是,缓存一致性也是一个需要考虑的问题,项目可能...
8. **抢红包算法**:实现无队列抢红包,可以使用分布式锁避免并发问题,保证公平性。 9. **Java内存模型与垃圾回收**:Java内存模型JMM规定了线程如何访问共享变量,垃圾回收机制主要通过可达性分析算法识别无用...
Java中的Redis或Zookeeper可以实现分布式锁,确保同一时刻只有一个请求能够修改库存。 3. **缓存策略**:为减轻数据库压力,可采用缓存技术,如Redis,将热门商品信息和用户状态暂存于内存中,提高查询速度。同时,...
这涉及到线程池管理、锁的使用、非阻塞I/O等技术,参赛者需对这些并发控制机制有深入理解。 3. **数据缓存**:为了减少数据库访问带来的延迟,中间件通常会引入缓存机制,如Redis或Memcached。优化缓存策略,如LRU...