自实现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的缩写,中文含义:比较交 ...
随机推荐
- How can i use iptables save on centos 7?
I installed CentOS 7 with minimal configuration (os + dev tools). I am trying to open 80 port for ht ...
- Android中下载、安装和卸载(原)
应用场景:在检查版本更新的时候经常需要从服务器端下载然后安装到手机中 使用工具: XUtils,这个开源的框架真的是需要花大把时间去阅读和理解的,十分有用的,on the way ! fighting ...
- nginx限制单个IP访问配置
最近公司做了一个砸金蛋的活动,经过几天的发酵宣传后,每天以几万的的用户数在增长,后面才发现原来有人专门为此开发了一个全自动注册的软件 一时间网站被刷得打开异常缓慢,查看日志发现大部分都是用软件在刷,于 ...
- sudo add-apt-repository no found解决方法
sudo apt-get install python-software-propertiessudo apt-get install software-properties-common
- MYSQL数据库导入数据时出现乱码的解决办法
我的一个网站在负载搞不定的情况下最终选择了数据库和程序分离的方式解决的高负载,但是再导入数据的时候出现了大量乱码,最终通过方法二解决掉导入数据的问题,后面再设计网站布局的时候数据库跟网站程序分离是个很 ...
- 计算ffff:0~ffff:b数据的和,结果存在dx中
代码: assume cs:sad sad segment start: mov ax, 0ffffh mov ds, ax mov dx, ; add ds:0bh~ds:1h mov cx, 0b ...
- iOS、swift、React Native学习常用的社区、论坛
<!----iOS> <!----Swift>*IOS开发常用社区:http://code4app.com/ *IOS开发常用社区:http://www.cocoachina. ...
- Codeforces Round #341 Div.2 D. Rat Kwesh and Cheese
嗯本来想着直接算出来不就行了吗 然后我想到了200^200^200....... 好吧其实也不难取两次log就行了 然后我第一次写出来log就写残了........... log里面的拆分要仔细啊.. ...
- T24基础-基本命令(1)
如果你不知道什么是T24,那这篇文章对你意义不大.如果你所在银行IT刚好就准备使用或已经使用T24作为银行核心系统,那我的文章对你会很有帮助. 1. LIST 这个语句相当于SQL里的“select ...
- C语言字符串操作总结大全(超详细)
本篇文章是对C语言字符串操作进行了详细的总结分析,需要的朋友参考下 1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat( ...