加锁
临时节点是随着客户端失去连接而删除,所以不会因客户端宕机而造成锁无法释放
释放锁
如果是多个线程也是如此的,线程B监听前一个节点(线程A创建的)的状态,线程C监听前一个节点(线程B创建)的状态,这样就像形成了一个等待队列
分布式锁 | 优点 | 缺点 |
---|---|---|
redis | set和del执行效率高 | 没有等待锁的队列,只能通过自旋的形式来等待锁。 实现复杂,需要考虑原子性、超时、误删等情形。 |
zookeeper | 有等待锁的队列,提高抢占锁的效率 | 添加和删除节点效率低下 |
redis生产环境上一般使用redission来实现分布式锁
zookeeper一般使用apache curator来实现分布式锁
apache curator教程
- 添加依赖
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>3.2.1</version>
</dependency>
- 关键代码
public class A {
private static CuratorFramework client = getClient();
private static InterProcessMutex lock = new InterProcessMutex(client, "/test");
public static int count = 0;
public void doSomething() {
try {
lock.acquire();
System.out.println(Thread.currentThread().getName() + "begin----------------------");
System.out.println(Thread.currentThread().getName() + "----------------------");
System.out.println(Thread.currentThread().getName() + "end----------------------");
count++;
lock.release();
} catch (Exception e) {
e.printStackTrace();
}
}
private static CuratorFramework getClient() {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("127.0.0.1:2181")
.retryPolicy(retryPolicy)
.sessionTimeoutMs(6000)
.connectionTimeoutMs(3000)
.namespace("demo")
.build();
client.start();
return client;
}
public static void closeZk() {
if (client != null) {
client.close();
}
}
}
- Test
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
new A().doSomething();
}
};
for (int i = 0; i < 50; i++) {
new Thread(runnable).start();
}
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + A.count);
A.closeZk();
}
核心代码就三行
InterProcessMutex lock = new InterProcessMutex(client, "/lock_name");
lock.acquire(); //加锁
//业务代码
lock.release(); //释放锁
zookeeper增删查改
public class SimpleDemo {
private static final int SESSION_TIMEOUT = 20000;
ZooKeeper zk;
Watcher wh = new Watcher() {
@Override
public void process(WatchedEvent event){
System.out.println("@@@@@@@@@@@@@" + event.toString());
}
};
private void createZKInstance() throws IOException {
zk = new ZooKeeper("localhost:2181", SimpleDemo.SESSION_TIMEOUT, this.wh);
}
private void ZKOperations() throws KeeperException, InterruptedException {
zk.create("/zoo2", "myData2".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(new String(zk.getData("/zoo2", false, null)));
zk.setData("/zoo2", "shenlan2423".getBytes(), -1);
System.out.println(new String(zk.getData("/zoo2", false, null)));
zk.delete("/zoo2", -1);
System.out.println(zk.exists("/zoo2", false));
}
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
SimpleDemo dm = new SimpleDemo();
dm.createZKInstance();
dm.ZKOperations();
dm.ZKClose();
}
private void ZKClose() throws InterruptedException {
zk.close();
}
}
zookeeper不使用框架实现分布式锁
public class DistributedClient {
private static final int SESSION_TIMEOUT = 5000;
private String hosts = "localhost:2181";
private String groupNode = "locks";
private String subNode = "sub";
private ZooKeeper zk;
private String thisPath;
private String waitPath;
private CountDownLatch latch = new CountDownLatch(1);
private void connectZookeeper() throws Exception {
zk = new ZooKeeper(hosts, SESSION_TIMEOUT, (event) -> {
try {
if (event.getState() == Watcher.Event.KeeperState.SyncConnected) {
latch.countDown();
}
if (event.getType() == Watcher.Event.EventType.NodeDeleted && event.getPath().equals(waitPath)) {
doSomething();
}
} catch (Exception e) {
e.printStackTrace();
}
});
latch.await();
thisPath = zk.create("/" + groupNode + "/" + subNode, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
Thread.sleep(10);
List<String> children = zk.getChildren("/" + groupNode, false);
if (children.size() == 1) {
doSomething();
} else {
String thisNode = thisPath.substring(("/" + groupNode + "/").length());
Collections.sort(children);
int index = children.indexOf(thisNode);
if (index == -1) {
} else if (index == 0) {
doSomething();
} else {
this.waitPath = "/" + groupNode + "/" + children.get(index - 1);
zk.getData(waitPath, true, new Stat());
}
}
}
private void doSomething() throws KeeperException, InterruptedException {
System.out.println("gain lock: " + thisPath);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("finished: " + thisPath);
zk.delete(this.thisPath, -1);
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
new Thread() {
@Override
public void run() {
try {
DistributedClient dl = new DistributedClient();
dl.connectZookeeper();
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
Thread.sleep(Long.MAX_VALUE);
}
}