总共有三种方法,分别是通过mysql数据库,通过Redis和通过zookeeper
①mysql是创建一个中间表,以记录是否对业务唯一ID上锁,未上锁的--给上锁--处理业务逻辑--最后释放锁,上锁只能返回或者等待;
②Redis是通过String类型的setnx(set if not exists)实现的,命令成功返回1执行业务逻辑,命令失败返回0等待释放锁. 底层是因为Redis是单线程的,并发进程扔需要排队执行,故最终谁先执行了setnx谁获得锁. 现在可以通过Redisson框架实现.
public static void main(String[] args) throws Exception {
Redisson redisson = Redisson.create();
RLock lock = redisson.getLock("haogrgr");
lock.lock();
try {
System.out.println("hagogrgr");
}
finally {
lock.unlock();
}
redisson.shutdown();
}
③zookeeper是根据其有序性各个客户端创建临时节点,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁,否则监听刚好在自己之前一位的子节点删除消息,获得子节点变更通知后重复此步骤直至获得锁;第三方工具Curator
public static void main(String[] args) throws Exception {
//创建zookeeper的客户端
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
CuratorFramework client = CuratorFrameworkFactory.newClient("10.21.41.181:2181,10.21.42.47:2181,10.21.49.252:2181", retryPolicy);
client.start();
//创建分布式锁, 锁空间的根节点路径为/curator/lock
InterProcessMutex mutex = new InterProcessMutex(client, "/curator/lock");
mutex.acquire();
//获得了锁, 进行业务流程
System.out.println("Enter mutex");
//完成业务流程, 释放锁
mutex.release();
//关闭客户端
client.close();
}
可以看到关键的核心操作就只有mutex.acquire()和mutex.release(),简直太方便了!
下面来分析下获取锁的源码实现。acquire的方法如下:
/*
* 获取锁,当锁被占用时会阻塞等待,这个操作支持同线程的可重入(也就是重复获取锁),acquire的次数需要与release的次数相同。
* @throws Exception ZK errors, connection interruptions
*/
@Override
public void acquire() throws Exception
{
if ( !internalLock(-1, null) )
{
throw new IOException("Lost connection while trying to acquire lock: " + basePath);
}
}