自实现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的缩写,中文含义:比较交 ...
随机推荐
- Linux mint 18版本开启SSH服务
linux mint 18版本默认是没有安装ssh server的 需要手动安装 安装ssh server: 此命令需要联网,会自动下载安装 安装之后看是否开始了ssh, 看到ssh-agent 和s ...
- Hql 中 dao 层 以及daoimpl 层的代码,让mvc 模式更直观简洁
1.BaseDao接口: //使用BaseDao<T> 泛型 ,在service中注入的时候,只需要将T换为对应的bean即可 public interface BaseDao<T& ...
- 为sproto手写了一个python parser
这是sproto系列文章的第三篇,可以参考前面的<为sproto添加python绑定>.<为python-sproto添加map支持>. sproto是云风设计的序列化协议,用 ...
- JSP基础总结(运行机制、脚本元素、指令元素、动作元素)
JSP的运行机制: 1.转译阶段:JSP页面转换成Servlet类: 2.请求阶段:Servlet类执行,将相应结果发送至客户端. 流程解释: 1.用户访问某个JSP页面 2.服务器找到相应的JSP页 ...
- Java设计模式(三) 抽象工厂模式
原创文章,同步发自作者个人博客,转载请注明出处 http://www.jasongj.com/design_pattern/abstract_factory/ 抽象工厂模式解决的问题 上文<工厂 ...
- php session的理解与使用
session学习参考链接:1.http://www.w3cschool.cn/php_sessions.html:2.http://php.net/manual/zh/book.session.ph ...
- oracle信息统计
优化器统计范围: 表统计: --行数,块数,行平均长度:all_tables:NUM_ROWS,BLOCKS,AVG_ROW_LEN:列统计: --列中唯一值的数量(NDV),NULL值的数量,数据分 ...
- HTML5 地理位置定位(HTML5 Geolocation)原理及应用
地理位置(Geolocation)是 HTML5 的重要特性之一,提供了确定用户位置的功能,借助这个特性能够开发基于位置信息的应用.今天这篇文章向大家介绍一下 HTML5 地理位置定位的基本原理及各个 ...
- [Beta] postmortem
设想和目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 解决网站前端的数据处理以及获取问题,定义的很清楚,对于典型用户也比较清晰,因为主要只有一个用户,所以对于 ...
- python 3 学习笔记(一)
由于之前学过python2,因此今天就想记录下第一天学习python3过程中的遇到的不同和之前没有太掌握的基础知识. python2和python3的语法区别 print语句 在Python2里,pr ...