抽象的OrderService接口:
public interface OrderService {
void createOrder();
}
订单号生成类(模拟公共资源ps:需要用锁的地方):
import java.text.SimpleDateFormat;
import java.util.Date;
public class OrderCodeGenerator {
private static int i = 0;
public String getOrderCode() {
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-");
return sdf.format(now) + ++i;
}
}
MyZkSerializer 继承了ZkSerializer:
import java.io.UnsupportedEncodingException;
import org.I0Itec.zkclient.exception.ZkMarshallingError;
import org.I0Itec.zkclient.serialize.ZkSerializer;
public class MyZkSerializer implements ZkSerializer {
@Override
public Object deserialize(byte[] bytes) throws ZkMarshallingError {
try {
return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new ZkMarshallingError(e);
}
}
@Override
public byte[] serialize(Object obj) throws ZkMarshallingError {
try {
return String.valueOf(obj).getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new ZkMarshallingError(e);
}
}
}
自己实现的可重入锁ZkReentrantLock:
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
/**
* 分布式可重入锁
*/
public class ZkReentrantLock implements Lock {
private ZkClient client;
//可提取为配置中,
private static final String LOCK_PATH = "/REENT_LOCK";
private String currentPath;
private String beforePath;
// 线程获取锁的次数
private static volatile int state;
// 当前获取锁的线程
private static volatile Thread thread;
public ZkReentrantLock() {
super();
client = new ZkClient("10.200.111.102:2181");
client.setZkSerializer(new MyZkSerializer());
if (!client.exists(LOCK_PATH)) {
try {
client.createPersistent(LOCK_PATH);
} catch (Exception e) {
}
}
}
@Override
public boolean tryLock() {
return tryLock(1);
}
public boolean tryLock(int acquires) {
// 获取当前线程
final Thread currntThread = Thread.currentThread();
// 获取当前锁的次数
int state = getState();
// state == 0 表示没有线程获取锁 进来的线程肯定能获取锁
if (state == 0) {
if (compareAndSetState(0, acquires, currntThread)) {
return true;
}
// state != 0 表示线程被获取了 判断是否是当前线程 如果是则 state+1 ,否则返回false
} else if (currntThread == getThread()) {
int nextS = getState() + acquires;
if (nextS < 0)
throw new Error("Maximum lock count exceeded");
// 这里不需要cas 原因不解释
setState(nextS);
System.out.println(Thread.currentThread().getName() + ":获取重入锁成功,当前获取锁次数: " + getState());
return true;
}
// 获取锁的不是当前线程
return false;
}
public boolean compareAndSetState(int expect, int update, Thread t) {
if (this.currentPath == null) {
currentPath = this.client.createEphemeralSequential(LOCK_PATH + "/", "1");
}
// 获取所有节点
List<String> children = this.client.getChildren(LOCK_PATH);
// 排序
Collections.sort(children);
// 判断当前节点是否为最小节点
if (getState() == expect && currentPath.equals(LOCK_PATH + "/" + children.get(0))) {
setState(update);
thread = t;
System.out.println(Thread.currentThread().getName() + ":获取锁成功,当前获取锁次数: " + getState());
return true;
}
// 取得前一个
// 得到字节的索引号
int curIndex = children.indexOf(currentPath.substring(LOCK_PATH.length() + 1));
beforePath = LOCK_PATH + "/" + children.get(curIndex - 1);
return false;
}
public final boolean tryRelease(int releases) {
// 可以判断是否自己获得锁 自己获得锁才能删除
final Thread currentThread = Thread.currentThread();
// 获取锁的不是当前线程
if (currentThread != getThread()) {
// throw new IllegalMonitorStateException();
return false;
}
// 释放锁的次数
int nextS = getState() - releases;
boolean free = false;
if (nextS == 0) {
free = true;
setThread(null);
// 删除zk节点
client.delete(currentPath);
System.out.println(Thread.currentThread().getName() + ": 所有锁释放成功:删除zk节点...");
}
setState(nextS);
if (!free)
System.out.println(Thread.currentThread().getName() + ": 释放重入锁成功: 剩余锁次数:" + getState());
return free;
}
@Override
public void lock() {
if (!tryLock()) {
// 没有获得锁,阻塞自己
waitForLock();
// 再次尝试加锁
lock();
}
}
private void waitForLock() {
CountDownLatch cdl = new CountDownLatch(1);
IZkDataListener listener = new IZkDataListener() {
@Override
public void handleDataChange(String arg0, Object arg1) throws Exception {
}
@Override
public void handleDataDeleted(String arg0) throws Exception {
System.out.println("节点被删除了,开始抢锁...");
cdl.countDown();
}
};
// 完成watcher注册
this.client.subscribeDataChanges(beforePath, listener);
// 阻塞自己
if (this.client.exists(beforePath)) {
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 取消注册
this.client.unsubscribeDataChanges(beforePath, listener);
}
@Override
public void unlock() {
// 可以判断是否自己获得锁 自己获得锁才能删除
// client.delete(currentPath);
tryRelease(1);
}
public static int getState() {
return state;
}
public static void setState(int state) {
ZkReentrantLock.state = state;
}
public static Thread getThread() {
return thread;
}
public static void setThread(Thread thread) {
ZkReentrantLock.thread = thread;
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
// TODO Auto-generated method stub
return false;
}
@Override
public Condition newCondition() {
// TODO Auto-generated method stub
return null;
}
}
OrderServiceImplWithZkDis 实现了 OrderServiceI接口,并且测试方法也写在了里面:
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.locks.Lock;
/**
* 业务实现,需要加锁的地方
*/
public class OrderServiceImplWithZkDis implements OrderService {
private static OrderCodeGenerator org = new OrderCodeGenerator();
//private Lock lock = new ZookeeperDisLock("/LOCK_TEST");
private Lock lock = new ZkReentrantLock();
@Override
public void createOrder() {
String orderCode = null;
try {
lock.lock();
orderCode = org.getOrderCode();
TestReLock();
System.out.println(Thread.currentThread().getName() + "生成订单:" + orderCode);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void TestReLock() {
lock.lock();
System.out.println(Thread.currentThread().getName() + "测试重入锁成功...");
lock.unlock();
}
public static void main(String[] args) {
int num = 20;
CyclicBarrier cyclicBarrier = new CyclicBarrier(num);
for (int i = 0; i < num; i++) {
new Thread(new Runnable() {
@Override
public void run() {
OrderService orderService = new OrderServiceImplWithZkDis();
System.out.println(Thread.currentThread().getName() + ": 我准备好了");
try {
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
orderService.createOrder();
}
}).start();
}
}
}
测试结果:

本文详细解析了使用Zookeeper实现分布锁的原理与代码实践,包括创建可重入的ZkReentrantLock类,实现线程安全的订单号生成,以及通过Zookeeper的临时顺序节点确保分布式环境下锁的唯一性和公平性。

588

被折叠的 条评论
为什么被折叠?



