一、排他锁与共享锁
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。分为排他锁和共享锁。
排他锁
排他锁(Exclusive Locks, 简称 X 锁),又称为写锁或独占锁,是一种基本的锁类型。如果事务T1对数据对象O1加上了排他锁,那么在整个加锁期间,只允许事务T1对O1进行读取和更新操作,其他任何事务都不能再对这个数据对象进行任何类型的操作——直到T1释放了排他锁。
共享锁
共享锁(Shared Locks,简称S锁),又称为读锁。允许一个资源可以被多个读操作访问,或者被一个写操作访问,但两者不能同时进行。(《从PAXOS到ZOOKEEPER分布式一致性原理与实践》中6.17节对共享锁的定义描述实在是不严谨,所以不引用了)。
书上对如何利用Zookeeper实现排他锁和共享锁的描述倒是很清晰易懂的。
对于实现排他锁,简单地说就是多个客户端同时去竞争创建同一个临时子节点,Zookeeper能够保证只有一个客户端创建成功,那么这个创建成功的客户端就获得排他锁。正常情况下,这个客户端执行完业务逻辑会删除这个节点,也就是释放了锁。如果该客户端宕机了,那么这个临时节点会被自动删除,锁也会被释放。
流程图如下:
对于共享锁,则麻烦一些,因为涉及到是读操作还是写操作的问题。所有的客户端都会到某个节点,例如:/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。
流程图如下:
二、排他锁的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: <br/>
* Intention: <br/>
* <p>
* Class Name: com.my.CuratorTest.RecipesSharedLockTest<br/>
* Create Date: 2017/8/20 17:07 <br/>
* Project Name: MyTest <br/>
* Company: All Rights Reserved. <br/>
* Copyright © 2017 <br/>
* </p>
* <p>
* author: GaoWei <br/>
* 1st_examiner: <br/>
* 2nd_examiner: <br/>
* </p>
*
* @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: <br/>
* Intention: <br/>
* <p>
* Class Name: com.my.CuratorTest.RecipesSharedLockTest<br/>
* Create Date: 2017/8/20 17:07 <br/>
* Project Name: MyTest <br/>
* Company: All Rights Reserved. <br/>
* Copyright © 2017 <br/>
* </p>
* <p>
* author: GaoWei <br/>
* 1st_examiner: <br/>
* 2nd_examiner: <br/>
* </p>
*
* @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分布式一致性原理与实践