生于忧患 发表于 2016-6-25 22:56

申请会员ID:生于忧患





申请会员ID:生于忧患

邮箱:550084490@qq.com
希望加入52pojie大家庭,今天浅谈下


并发锁,希望管理员予以通过
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


当然,笔者在一个项目中,这样使用锁感觉很麻烦。

于是笔者写了一个方法:

http://qqadapt.qpic.cn/adapt/0/56f8195a-54d8-9203-0967-2e789285600c/800?pt=0&ek=1&kp=1&sce=0-12-12

调用:

http://qqadapt.qpic.cn/adapt/0/1ef7dc14-77e5-343f-87bf-ccb3b41b38bc/800?pt=0&ek=1&kp=1&sce=0-12-12



一行代码带锁调用,是不是很爽?

以下贴上该工具类所有代码:

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);
            }
      }
    }
}
感谢管理员耐心看完,望审核通过





Hmily 发表于 2016-6-27 16:04

http://blog.51duobei.com/article_2.html 这个是你发布的?
页: [1]
查看完整版本: 申请会员ID:生于忧患