自实现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();
}
}
}
我觉得第二种实现不能直接放入业务代码中,稍作修改应该可以。
自实现CAS原理JAVA版,模拟下单库存扣减的更多相关文章
- Java练习demo 20190402 优惠券扣减
实体类: package org.jimmy.autosearch2019.pojo; import java.math.BigDecimal; public class TestEntity2019 ...
- java版模拟浏览器下载百度动漫图片到本地。
package javaNet.Instance.ImageDownload; import java.io.BufferedReader; import java.io.File; import j ...
- J.U.C Atomic(一)CAS原理
CAS概念 CAS:Compare And Swap,比较并交换.java.util.concurrent包完全是建立于CAS机制之上的. CAS原理 Java CAS是通过调用Unsafe的nati ...
- JAVA CAS原理深度分析-转载
参考文档: http://www.blogjava.net/xylz/archive/2010/07/04/325206.html http://blog.hesey.net/2011/09/reso ...
- JAVA CAS原理
转自: http://blog.csdn.net/hsuxu/article/details/9467651 CAS CAS: Compare and Swap java.util.concurren ...
- 【转】JAVA CAS原理深度分析
java.util.concurrent包完全建立在CAS之上的,没有CAS就不会有此包.可见CAS的重要性. CAS CAS:Compare and Swap, 翻译成比较并交换. java.uti ...
- JAVA CAS原理深度分析
参考文档: http://www.blogjava.net/xylz/archive/2010/07/04/325206.html http://blog.hesey.net/2011/09/reso ...
- JAVA CAS原理深度分析(转)
看了一堆文章,终于把JAVA CAS的原理深入分析清楚了. 感谢GOOGLE强大的搜索,借此挖苦下百度,依靠百度什么都学习不到! 参考文档: http://www.blogjava.net/xylz/ ...
- Java中的CAS原理
前言:在对AQS框架进行分析的过程中发现了很多CAS操作,因此有必要对CAS进行一个梳理,也便更清楚的了解其原理. 1.CAS是什么 CAS,是compare and swap的缩写,中文含义:比较交 ...
随机推荐
- Baskets of Gold Coins_暴力
Problem Description You are given N baskets of gold coins. The baskets are numbered from 1 to N. In ...
- 第三个Sprint团队贡献分
201306114322 邵家文 50分 201306114319 陈俊金 10分 201306114320 李新 10分 201306114324 朱浩龙 10分
- 《一个 Go 程序系统线程暴涨的问题》结论
原文地址:https://zhuanlan.zhihu.com/p/22474724 作者的结论没写好,我来说两句.. 结论: Docker swarm自己有个函数,叫setTcpUserTimeou ...
- CSS+DIV 设计一个简单的个人网页界面
*{ margin:0px; padding:0px; } body{ background:#e5e6d0; } #header,#menu,#banner,#main,#footer{ margi ...
- HashMap归档-超越昨天的自己系列
java HashMap 读一下源码,一个数组存储数据: transient Entry[] table; 内部存key和value的内部类: static class Entry<K,V> ...
- QML Image: Cannot open: qrc:///new.pic.png
初次遇到这个问题真有点摸不着头脑,于是乎百度一下咯,但是百度一向没有什么用,该有的没有,没用的回答倒是有特么一大堆. 自己解决: 我的解决方法很简答: 第一步:把图片放到当前路径下,也就是和.pro一 ...
- 此博客主人已搬家访问新家地址:http://write.blog.csdn.net/postlist
此博客主人已搬家访问新家地址:http://write.blog.csdn.net/postlist
- Tomcat口令猜解工具【Python脚本】
Tomcat 服务器网页部署,登录需用户名/密码,编写了一个简单的Python脚本来测试一些简单的弱口令. 测试环境:Tomcat版本 7.0 登录界面采用basic认证,Base 64加密一下,模拟 ...
- pthread_create 内存释放
run() { pthread_attr_destroy(&m_attr); pthread_detach(pthread_self()); }
- [Beta] postmortem
设想和目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 解决网站前端的数据处理以及获取问题,定义的很清楚,对于典型用户也比较清晰,因为主要只有一个用户,所以对于 ...