自实现CAS原理JAVA版,模拟下单库存扣减

在做电商系统时,库存是一个非常严格的数据,根据CAS(check and swap)原来下面对库存扣减提供两种方法,一种是redis,一种用java实现CAS。

第一种 redis实现:

以下这个类是工具类,稍作修改就可运行

import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCommands;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.util.Pool;

import com.maowu.commons.conf.FileUpdate;
import com.maowu.commons.conf.NotifyFileUpdate;
import com.maowu.commons.logutil.LogProxy;
import com.maowu.commons.util.LogicUtil;

public class JedisPoolFactory
{
    private static Logger log = LogProxy.getLogger(JedisPoolFactory.class);

private static String configFile = "jedisconf";
    /**
     * 主服务器数据源连接池,主要用于写操作
     */
    private static JedisPool jedisPool = null;

/**
     * 数据源共享连接池,主要用于读取数据
     */
    private static ShardedJedisPool shardedJedisPool = null;

static
    {
        loadXmlConfig();

NotifyFileUpdate.registInterface(new FileUpdate()
        {
            @Override
            public void updateFile(String fileName)
            {
                if (fileName.startsWith(configFile))
                {
                    log.debug("updateFile = " + fileName);
                    loadXmlConfig();
                }
            }
        });
    }

/**
     * @author: smartlv
     * @date: 2014年2月17日下午3:36:30
     */
    private static void loadXmlConfig()
    {
        DefaultListableBeanFactory context = new DefaultListableBeanFactory();
        BeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("classpath:autoconf/jedisconf.xml");

initJedisPool(context);
        initShardedJedisPool(context);
    }

private static void initJedisPool(DefaultListableBeanFactory context)
    {
        JedisConf conf = (JedisConf) context.getBean("jedisConf");

JedisShardInfo jsInfo = null;
        if (LogicUtil.isNullOrEmpty(conf.getJsInfo()))
        {
            return;
        }
        jsInfo = conf.getJsInfo().get(0);

jedisPool = new JedisPool(conf.getPoolConfig(), jsInfo.getHost(), jsInfo.getPort(), jsInfo.getTimeout(),
                jsInfo.getPassword());
    }

private static void initShardedJedisPool(DefaultListableBeanFactory context)
    {
        JedisConf conf = (JedisConf) context.getBean("shardedJedisConf");

shardedJedisPool = new ShardedJedisPool(conf.getPoolConfig(), conf.getJsInfo(), conf.getAlgo(),
                Pattern.compile(conf.getPattern()));
    }

public static JedisPool getJedisPool()
    {
        return jedisPool;
    }

public static ShardedJedisPool getShardedJedisPool()
    {
        return shardedJedisPool;
    }

/**
     * 打开一个普通jedis数据库连接
     *
     * @param jedis
     */
    public static Jedis openJedis() throws Exception
    {
        if (LogicUtil.isNull(jedisPool))
        {
            return null;
        }

return jedisPool.getResource();
    }

/**
     * 打开一个分布式jedis从数据库连接
     *
     * @throws
     * @author: yong
     * @date: 2013-8-30下午08:41:23
     */
    public static ShardedJedis openShareJedis() throws Exception
    {
        if (LogicUtil.isNull(shardedJedisPool))
        {
            return null;
        }

return shardedJedisPool.getResource();
    }

/**
     * 归还普通或分布式jedis数据库连接(返回连接池)
     *
     * @param p
     *        连接池
     * @param jedis
     *        客户端
     */

@SuppressWarnings({ "rawtypes", "unchecked" })
    public static void returnJedisCommands(Pool p, JedisCommands jedis)
    {
        if (LogicUtil.isNull(p) || LogicUtil.isNull(jedis))
        {
            return;
        }

try
        {
            p.returnResource(jedis);// 返回连接池
        }
        catch (Exception e)
        {
            log.error("return Jedis or SharedJedis to pool error", e);
        }
    }

/**
     * 释放redis对象
     *
     * @param p
     *        连接池
     * @param jedis
     *        redis连接客户端
     * @throws
     * @author: yong
     * @date: 2013-8-31下午02:12:21
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static void returnBrokenJedisCommands(Pool p, JedisCommands jedis)
    {
        if (LogicUtil.isNull(p) || LogicUtil.isNull(jedis))
        {
            return;
        }

try
        {
            p.returnBrokenResource(jedis); // 获取连接,使用命令时,当出现异常时要销毁对象
        }
        catch (Exception e)
        {
            log.error(e.getMessage(), e);
        }
    }

}

redis 保证CAS的一个方法:

@Override
    public long decrLastActiSkuCount(String actiId, String skuId, long decrCount)
    {
        int NOT_ENOUGH = -2;// 库存不足
        long lastCount = -1;// 剩余库存
        int maxTryTime = 100;// 最大重试次数

Jedis jedis = null;
        JedisPool pool = JedisPoolFactory.getJedisPool();
        try
        {
            jedis = pool.getResource();

String key = actiId + Constants.COLON + skuId;
            byte[] bkey = SerializeUtil.serialize(key);
            LogUtil.bizDebug(log, "decr sku count key=[%s]", key);

for (int i = 0; i < maxTryTime; i++)
            {
                try
                {
                    jedis.watch(bkey);// 添加key监视
                    byte[] r = jedis.get(bkey);
                    long c = Long.valueOf(new String(r, Protocol.CHARSET));
                    if (c < decrCount)// 判断库存不充足
                    {
                        jedis.unwatch();// 移除key监视
                        return NOT_ENOUGH;// 库存不足
                    }
                    Transaction t = jedis.multi();// 开启事务,事务中不能有查询的返回值操作
                    t.decrBy(bkey, decrCount);// 修改库存数,该函数不能立即返回值
                    List<Object> trs = t.exec();// 事务执行结果
                    LogUtil.bizDebug(log, "transaction exec result=[%s]", trs);
                    if (LogicUtil.isNotNullAndEmpty(trs))
                    {
                        lastCount = (Long) trs.get(0);// 剩余库存
                        break;// 在多线程环境下,一次可能获取不到库存,当提前获取到库存时,跳出循环
                    }
                }
                catch (Exception e)
                {
                    log.error("watched key‘s value has changed", e);
                }

if (i == maxTryTime - 1)
                {
                    log.error("arrived max try time:" + maxTryTime);
                }
            }
        }
        catch (Exception e)
        {
            log.error("decr sku stock count error", e);
            JedisPoolFactory.returnBrokenJedisCommands(pool, jedis);
        }
        finally
        {
            JedisPoolFactory.returnJedisCommands(pool, jedis);
        }

return lastCount;
    }

第二种实现

数据类:

public class D
{
    public final static int INIT_VERSION = 0;
    volatile int c = 0;
    volatile int v = INIT_VERSION;

public synchronized int add(int c, int v)
    {
        try
        {
            Thread.sleep(1000);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        if (this.v == v)
        {
            this.c = this.c + c;
            this.v++;
            return this.c;
        }
        return -1;
    }

public int[] get()
    {
        try
        {
            Thread.sleep(1000);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        return new int[] { c, v };
    }
}

访问类:访问库存

public class T implements Runnable
{
    D d;
    int i = 0;

public T(D d, int i)
    {
        this.d = d;
        this.i = i;
    }

public void changeStock(D d, int c)
    {
        for (int i = 0; i < 100; i++)
        {
            int g[] = d.get();
            if (g[0] < Math.abs(c))
            {
                System.out.println("库存不足");
                break;
            }
            else
            {
                int a = d.add(c, g[1]);
                if (a >= 0)
                {
                    System.out.println("待扣库存-->" + c + " 剩余库存-->" + a);
                    break;
                }
                else
                {
                    System.out.println("待扣库存-->" + c + " 版本号不对");
                }
            }
        }
    }

@Override
    public void run()
    {
        changeStock(d, i);
    }

public static void main(String[] args)
    {
        // 初始化库存
        D d = new D();
        int c = d.add(200, D.INIT_VERSION);
        System.out.println("---初始化" + c + "库存---");

Thread th[] = new Thread[10];
        for (int i = 0; i < th.length; i++)
        {
            th[i] = new Thread(new T(d, -i), "i=" + i);
        }

for (int i = 0; i < th.length; i++)
        {
            th[i].start();
        }
    }
}

我觉得第二种实现不能直接放入业务代码中,稍作修改应该可以。

时间: 2024-12-03 04:38:51

自实现CAS原理JAVA版,模拟下单库存扣减的相关文章

java版模拟浏览器下载百度动漫图片到本地。

1 package javaNet.Instance.ImageDownload; 2 3 import java.io.BufferedReader; 4 import java.io.File; 5 import java.io.FileOutputStream; 6 import java.io.IOException; 7 import java.io.InputStream; 8 import java.io.InputStreamReader; 9 import java.net.M

[原创]商城系统下单库存管控系列杂记(一)(并发安全和性能基础认识)

商城系统下单库存管控系列杂记(一)(并发安全和性能基础认识) 前言 参与过几个中小型商城系统的开发,随着时间的增长,以及对系统的深入研究和测试,发现确实有很多值得推敲和商榷的地方(总有很多重要细节存在缺陷).基于商城系统,无论规模大小,或者本身是否分布架构,个人觉得最核心的一环就是下单模块,而这里面更相关和棘手的一些设计和问题,大多时候都涉及库存系统.想想之前跟某人的交流,他一句"库存管控做得好,系统设计就成功了一半",自己颇有认同.围绕这个点,结合目前经验和朋友间的交流(包括近来参阅

Java CAS原理

java.util.concurrent包完全建立在CAS之上的,没有CAS就不会有此包.可见CAS的重要性. CAS CAS:Compare and Swap, 翻译成比较并交换. java.util.concurrent包中借助CAS实现了区别于synchronouse同步锁的一种乐观锁. 本文先从CAS的应用说起,再深入原理解析. CAS应用 CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B.当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做. 非阻塞算法 (n

JAVA CAS原理深度分析(转)

看了一堆文章,终于把JAVA CAS的原理深入分析清楚了. 感谢GOOGLE强大的搜索,借此挖苦下百度,依靠百度什么都学习不到! 参考文档: http://www.blogjava.net/xylz/archive/2010/07/04/325206.html http://blog.hesey.net/2011/09/resolve-aba-by-atomicstampedreference.html http://www.searchsoa.com.cn/showcontent_69238.

JAVA CAS原理深度分析

参考文档: http://www.blogjava.net/xylz/archive/2010/07/04/325206.html http://blog.hesey.net/2011/09/resolve-aba-by-atomicstampedreference.html http://www.searchsoa.com.cn/showcontent_69238.htm http://ifeve.com/atomic-operation/ http://www.infoq.com/cn/ar

MapReduce原理——PageRank算法Java版

Page Rank就是MapReduce的来源,下文是一个简单的计算PageRank的示例. import java.text.DecimalFormat; /**  * Created by jinsong.sun on 2014/7/15.  */ public class PageRankCaculator {     public static void main(String[] args) {         double[][] g = calcG(genS(), 0.85);  

转:JAVA CAS原理深度分析

看了一堆文章,终于把Java CAS的原理深入分析清楚了. 感谢GOOGLE强大的搜索,借此挖苦下百度,依靠百度什么都学习不到! 参考文档: http://www.blogjava.net/xylz/archive/2010/07/04/325206.html http://blog.hesey.net/2011/09/resolve-aba-by-atomicstampedreference.html http://www.searchsoa.com.cn/showcontent_69238.

微博URL短网址生成算法原理及(java版、php版实现实例)

短网址(Short URL),顾名思义就是在形式上比较短的网址.通常用的是asp或者php转向,在Web 2.0的今天,不得不说,这是一个潮流.目前已经有许多类似服务,借助短网址您可以用简短的网址替代原来冗长的网址,让使用者可以更容易的分享链接. 例如:http://t.cn/SzjPjA 短网址服务,可能很多朋友都已经不再陌生,现在大部分微博.手机邮件提醒等地方已经有很多应用模式了,并占据了一定的市场.估计很多朋友现在也正在使用. 看过新浪的短连接服务,发现后面主要有6个字符串组成,于是第一个

【java项目实践】详解Ajax工作原理以及实现异步验证用户名是否存在+源码下载(java版)

一年前,从不知道Ajax是什么,伴随着不断的积累,到现在经常使用,逐渐有了深入的认识.今天,如果想开发一个更加人性化,友好,无刷新,交互性更强的网页,那您的目标一定是Ajax. 介绍 在详细讨论Ajax是什么之前,先让我们花一分钟了解一下Ajax做什么.如图所示: 如上图展示给我们的就是使用Ajax技术实现的效果.伴随着web应用的越来越强大而出现的是等待,等待服务器响应,等待浏览器刷新,等待请求返回和生成新的页面成为了程序员们的最最头疼的难题.随着Ajax的出现使web应用程序变得更完善,更友