Zookeeper学习笔记(四)分布式锁

一、排他锁与共享锁

分布式锁是控制分布式系统之间同步访问共享资源的一种方式。分为排他锁和共享锁。

排他锁

排他锁(Exclusive Locks, 简称 X 锁),又称为写锁或独占锁,是一种基本的锁类型。如果事务T1对数据对象O1加上了排他锁,那么在整个加锁期间,只允许事务T1对O1进行读取和更新操作,其他任何事务都不能再对这个数据对象进行任何类型的操作——直到T1释放了排他锁。

共享锁

共享锁(Shared Locks,简称S锁),又称为读锁。允许一个资源可以被多个读操作访问,或者被一个写操作访问,但两者不能同时进行。(《从PAXOS到ZOOKEEPER分布式一致性原理与实践》中6.17节对共享锁的定义描述实在是不严谨,所以不引用了)。

书上对如何利用Zookeeper实现排他锁和共享锁的描述倒是很清晰易懂的。

对于实现排他锁,简单地说就是多个客户端同时去竞争创建同一个临时子节点,Zookeeper能够保证只有一个客户端创建成功,那么这个创建成功的客户端就获得排他锁。正常情况下,这个客户端执行完业务逻辑会删除这个节点,也就是释放了锁。如果该客户端宕机了,那么这个临时节点会被自动删除,锁也会被释放。

流程图如下:

Zookeeper学习笔记(四)分布式锁_第1张图片


对于共享锁,则麻烦一些,因为涉及到是读操作还是写操作的问题。所有的客户端都会到某个节点,例如:/shared_lock 下创建一个临时顺序节点,如果是读请求,就会创建诸如 /shared_lock/192.168.0.1-R-0000000001 的节点,如果是写操作,则创建诸如 /shared_lock/192.168.0.1-W-0000000001 的节点。是否获取到共享锁,从以下四个步骤来判断:

1、创建完节点后,获取/shared_lock节点下的所有子节点,并对该节点注册子节点变更的watcher监听。

2、确定自己的节点序号在所有子节点中的顺序。

3、对于读请求:

如果没有比自己序号小的子节点,或是所有比自己序号小的子节点都是去请求,那么表明自己已经成功获取到了共享锁,同时开始执行读取逻辑。

如果比自己序号小的子节点中有写请求,那么就需要进入等待。

对于写请求:

如果自己不是序号最小的子节点,那么就需要进入等待。

4、接收到Watcher通知后,重复步骤1。

流程图如下:

Zookeeper学习笔记(四)分布式锁_第2张图片


二、排他锁的Curator实现

基本摘抄自书上的示例代码:

package com.my.CuratorTest;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock;
import org.apache.curator.retry.ExponentialBackoffRetry;

import java.util.concurrent.CountDownLatch;

/**
 * Title: 
* Intention:
*

* Class Name: com.my.CuratorTest.RecipesSharedLockTest
* Create Date: 2017/8/20 17:07
* Project Name: MyTest
* Company: All Rights Reserved.
* Copyright © 2017
*

*

* author: GaoWei
* 1st_examiner:
* 2nd_examiner:
*

* * @version 1.0 * @since JDK 1.7 */ public class RecipesSharedLockTest { static String lockPath = "/curator_recipes_lock_path"; static CuratorFramework client = CuratorFrameworkFactory.builder() .connectString("127.0.0.1:2181") .retryPolicy(new ExponentialBackoffRetry(1000, 3)).build(); static int data = 1; public static void main(String[] args) { client.start(); final InterProcessReadWriteLock lock = new InterProcessReadWriteLock(client, lockPath); final CountDownLatch down = new CountDownLatch(1); for (int i= 0;i< 30;i++) { final int k = i; new Thread(new Runnable() { @Override public void run() { if (k%2 == 0) { try { down.countDown(); lock.readLock().acquire(); System.out.println(System.nanoTime() + ", " + Thread.currentThread().getName() + " 获取到读锁,当前数据="+ data); lock.readLock().release(); } catch (Exception e) { e.printStackTrace(); } } else { try { down.countDown(); lock.writeLock().acquire(); data ++; System.out.println(System.nanoTime() + ", " + Thread.currentThread().getName() + " 获取到写锁,写入成功,当前数据="+ data); lock.writeLock().release(); } catch (Exception e) { e.printStackTrace(); } } } }).start(); } down.countDown(); } }


三、共享锁的Curator实现

package com.my.CuratorTest;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock;
import org.apache.curator.retry.ExponentialBackoffRetry;

import java.util.concurrent.CountDownLatch;

/**
 * Title: 
* Intention:
*

* Class Name: com.my.CuratorTest.RecipesSharedLockTest
* Create Date: 2017/8/20 17:07
* Project Name: MyTest
* Company: All Rights Reserved.
* Copyright © 2017
*

*

* author: GaoWei
* 1st_examiner:
* 2nd_examiner:
*

* * @version 1.0 * @since JDK 1.7 */ public class RecipesSharedLockTest { static String lockPath = "/curator_recipes_lock_path"; static CuratorFramework client = CuratorFrameworkFactory.builder() .connectString("127.0.0.1:2181") .retryPolicy(new ExponentialBackoffRetry(1000, 3)).build(); static int data = 1; public static void main(String[] args) { client.start(); final InterProcessReadWriteLock lock = new InterProcessReadWriteLock(client, lockPath); final CountDownLatch down = new CountDownLatch(1); for (int i= 0;i< 30;i++) { final int k = i; new Thread(new Runnable() { @Override public void run() { if (k%2 == 0) { try { down.countDown(); lock.readLock().acquire(); System.out.println(System.nanoTime() + ", " + Thread.currentThread().getName() + " 获取到读锁,当前数据="+ data); lock.readLock().release(); } catch (Exception e) { e.printStackTrace(); } } else { try { down.countDown(); lock.writeLock().acquire(); data ++; System.out.println(System.nanoTime() + ", " + Thread.currentThread().getName() + " 获取到写锁,写入成功,当前数据="+ data); lock.writeLock().release(); } catch (Exception e) { e.printStackTrace(); } } } }).start(); } down.countDown(); } }


参考:

1、从PAXOS到ZOOKEEPER分布式一致性原理与实践

你可能感兴趣的:(Zookeeper学习笔记(四)分布式锁)