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

本文介绍了在电商系统中如何使用 Java 实现库存扣减,提供了基于 Redis 的解决方案和自实现的 CAS(Compare and Swap)原理。通过 Jedis 工具类进行 Redis 操作,演示了在多线程环境下如何保证库存操作的正确性。
部署运行你感兴趣的模型镜像

标签:

在做电商系统时,库存是一个非常严格的数据,根据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 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();

}

}

}

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

标签:

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值