|
吾爱游客
发表于 2016-6-25 22:56
1:何为并发锁?并发锁有什么用?
首先呢,我们在实际的业务中,肯定有一些交易类业务。举几个最的例子。
NO1.一个商品库存只剩下十份,但是有一百个人在同时购买。那么后端的业务肯定是这样:检查商品库存、检查订单金额与余额/或直接第三方支付、完成订单并扣除库存。。。这个业务很常见,每个人都网购过,那么一百个人同时购买,会怎样呢?一百个人同时在检查库存,而每个人只买了一份,库存剩余十份,那么肯定会检查通过,走入第二步,第三步。最终导致库存为负数。
NO2.交易类业务非常常见,某个交易要10块钱,而用户剩下20,按理来说可以买两份,可是用户来个并发,在某些特定的情况下说不定就买了几十份。正常的用户自然不会出问题,可是如果用户是职业BUG审计手,在交易的时候抓个包,终止这个包并保存协议,然后多线程发出这个包,那么可以导致几百次交易同时进行。然而后端的业务是:检查交易金额、检查用户余额、进行业务处理。然而几百个并发同时进行,这时候用户有20块钱,商品只需要10元,肯定是检查通过的,那么这写同时检查通过的就造成了多次交易,最终余额刷到负数。
在这个业务基础上,有了并发锁的存在。通俗点讲。就是一个用户,只能同时进行一个交易。在这个交易的基础上,该用户的所有交易都要排队进行。
synchronized (ZKLockUtil.class) {
//进行业务处理
}
当然,这个也是锁,但这个锁不是并发锁,这个锁会锁住全系统所有用户,不能解决集群模式下的并发问题,而且这个锁效率极低。简单的说,在这个锁的前提下,如果有一百个用户,每个用户同时进行一百个交易,那么一万个统统排队;然而并发锁却非如此,一百个用户同时进行,每个用户的一百个交易再排队。
类似的业务也只有在并发锁的模式下才是合理的。完善的。才能支持大用户,才能在集群模式下保证数据安全。
2:并发锁如何能锁住,如何最合理
并发锁要锁住有很多种方案,每种方案都有一定的适用性。
NO1.
synchronized (CacheFinal.class) {
//进行业务处理
}
这个锁太牛逼了,单机部署绝对可以锁住,而且还能做到绝对安全。但是效率呢,效率呢,抢购、秒杀、抽奖你这个锁是在坑用户还是在坑老板,玩我么???
NO2.
//定义并发锁标志
String key=CacheFinal.USER_INFO_KEY +userId;
Boolean isRun=false;
synchronized (CacheFinal.class) {
isRun=(Boolean) CacheTimerHandler.getCache(key);
if(isRun!=null&&isRun==true){
System.out.println("该业务正在执行,本次业务放弃。");
return "请勿进行重复操作";
}
CacheTimerHandler.addCache(key, true);
}
try {
//进行业务操作
} catch (Exception e) {
e.printStackTrace();
}finally{
//移除执行标志
CacheTimerHandler.removeCache(key);
}
这是一个基于缓存的并发锁,CacheTimerHandler是缓存操作类。比方说redis、memcached
很多项目中是这样实现的并发锁,但是redis、memcached这些东西存在瓶颈,当内存被使用完,会不规则的释放一部分冷门资源,而如果这时候刚好释放了并发锁标志,用户进行并发情况下的交易操作,还是有出现数据安全的可能性的。不过一般使用缓存作为并发锁的项目,都有一个预估内存。也就是给缓存一个估值。确保缓存使用不会超过这个值。
NO3.
// 分布式锁
InterProcessMutex lock = null;
CuratorFramework client = null;
try {
client = DistributedNewLock.getClient(lockServer);
// 用户锁
lock = DistributedNewLock.getLock(client, lockKey);
//获取锁许可证
if (!lock.acquire(5 * 1000, TimeUnit.SECONDS)) {
return false;
}
//进行业务操作
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放锁
closeLock(lock);
closeClient(client);
}
这个锁,就比较专业了,zookeeper,本身更大的意义就是为了对并发锁做处理的。阿里巴巴为了方便对zookeeper进行操作,还专门封装了框架叫做dubbo,当然,以上代码用到的是Curator。所需JAR:curator-client-2.10.0.jar curator-framework-2.10.0.jar curator-recipes-2.10.0.jar
当然,笔者在一个项目中,这样使用锁感觉很麻烦。
于是笔者写了一个方法:
调用:
一行代码带锁调用,是不是很爽?
以下贴上该工具类所有代码:
package com.scrum.net.comm.util;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.log4j.Logger;
import com.scrum.net.comm.zk.lock.DistributedNewLock;
/**
* @remark 并发锁方法调用工具类
* @author 公子
* @time 2016-03-11
*/
public class ZKLockUtil {
protected static final Logger logger = Logger.getLogger(ZKLockUtil.class);
/**
* 基于分布式锁反射调用方法
* @param targeObj 目标对象
* @param methodName 目标方法名
* @param lockServer 并发锁服务器
* @param lockKey 并发锁KEY
* @param paras 方法参数
* @return
*/
public static Object invokMethod(Object targeObj, String methodName,
String lockServer, String lockKey, Object... paras) {
// 分布式锁
InterProcessMutex lock = null;
CuratorFramework client = null;
try {
client = DistributedNewLock.getClient(lockServer);
// 用户锁
lock = DistributedNewLock.getLock(client, lockKey);
//寻找方法
Method method=getTargeMethod(targeObj.getClass().getDeclaredMethods(), methodName, paras);
if(ObjectUtil.hasNull(method)){
method=getTargeMethod(targeObj.getClass().getMethods(), methodName, paras);
}
if(ObjectUtil.hasNull(method)){
System.out.println("方法不存在:"+targeObj.getClass().getName()+"."+methodName);
return null;
}
//获取锁许可证
if (!lock.acquire(5 * 1000, TimeUnit.SECONDS)) {
return false;
}
method.setAccessible(true);
//执行方法
return method.invoke(targeObj, paras);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放锁
closeLock(lock);
closeClient(client);
}
return null;
}
/**
* 从对象中获取目标方法
* @param methods 方法数组
* @param methodName 方法名称
* @param paras 参数列表
* @return
*/
private static Method getTargeMethod(Method []methods,String methodName,Object...paras){
for (Method m:methods) {
if(isTargeMethod(m, methodName, paras)){
return m;
}
}
return null;
}
/**
* 判断目标是否是当前方法
* @param method 当前方法
* @param methodName 目标方法名
* @param paras 目标方法参数列表
* @return
*/
private static boolean isTargeMethod(Method method,String methodName,Object...paras){
if(!method.getName().equals(methodName)){
return false;
}
Class[] clas=method.getParameterTypes();
if(ObjectUtil.isNullOrEmpty(clas)&&ObjectUtil.isNullOrEmpty(paras)){
return true;
}
if(ObjectUtil.isNullOrEmpty(clas)||ObjectUtil.isNullOrEmpty(paras)){
return false;
}
if(clas.length!=paras.length){
return false;
}
for (int i=0;i System.out.println(paras.getClass().getName());
System.out.println(clas.getName());
if(!(clas.getName().equals(paras.getClass().getName()))){
return false;
}
}
return true;
}
//释放连接
private static void closeClient(CuratorFramework client) {
if (client != null) {
DistributedNewLock.closeClient(client);
}
}
//释放锁
private static void closeLock(InterProcessMutex lock) {
// 释放锁
if (lock != null) {
try {
lock.release();
} catch (Exception e) {
logger.error("释放失败", e);
}
}
}
}
|
|
|
发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。 |
|
|
|
|