java redis 分布式_【redis】分布式锁实现,与分布式定时任务

如果你还不知道redis的基本命令与基本使用方法,请看

写在前面

redis辣么多数据结构,这么多命令,具体一点,都可以应用在什么场景呢?用来解决什么具体的问题?

分布式锁

redis是网络单线程的,它只有一个线程负责接受请求,这个特性即降低了redis本身的开发成本,也提高了redis的可用性。

分布式环境下,数据一致性问题一直是一个比较重要的话题,分布式与单机情况下最大的不同在于其不是多线程而是多进程。

多线程由于可以共享堆内存,因此可以简单的采取内存作为标记存储位置,例如cas,java的synchronize。而进程之间可能不在同一台物理机上,因此需要将标记存储在一个所有进程都能看到的地方。

常见的场景,秒杀场景中的库存超卖问题、多机定时任务的并发执行问题等。

库存超卖问题

假如订单服务部署了多个实例。

现在做一个商品秒杀活动,商品一共只有2个,同时购买的用户则可能有几千上万。

理想状态下第一个和第二个用户能购买成功,其他用户提示购买失败,

实际可能出现的情况是,多个用户都同时查到商品还没卖完,第一个用户买到,更新库存之前,第二个用户又下了订单,导致出错。

下面用java代码做一个演示:

java实例都可以被正常运行在jdk1.8+,使用jedis连接redis实例

importredis.clients.jedis.Jedis;importredis.clients.jedis.JedisPool;importredis.clients.jedis.JedisPoolConfig;/*** JedisPool连接

*@authortaifeng zhang

**/

public classJedisPoolConnect {public staticJedisPool jedispool;/*** 连接并返回jedis实例

**/

public staticJedis connectJedis () {if (jedispool == null) {

JedisPoolConfig jedisPoolConfig= newJedisPoolConfig();

jedisPoolConfig.setMinIdle(1);

jedisPoolConfig.setMaxIdle(10);

jedisPoolConfig.setTestOnBorrow(true);

jedispool= new JedisPool(jedisPoolConfig, "127.0.0.1", 6379);

}returnjedispool.getResource();

}

}import redis.clients.jedis.*;importredis.clients.jedis.Jedis;/*** 一个简单的超卖演示程序

**/

public classMarketWrong {public static String GOODS_LEN_KEY = "jedis:market:demo";private final Integer DECR_THREAD_LEN = 16;public voidsuperMarket () {//开线程去减库存

int i =DECR_THREAD_LEN;while (i > 0) {new Thread(() ->{boolean hasGoods = true;while (hasGoods) { //当库存大于0的时候

int goodsLen =getGoodsLen();if (goodsLen > 0) {

decrGoodsLen();//一般进来之后就直接减去库存了

System.out.println("现在库存为" +getGoodsLen());try{

Thread.sleep(100); //模拟中间处理流程

} catch(Exception e) {

System.out.println("执行减库存错误" + e.getMessage() + e.getLocalizedMessage() +e.getStackTrace());

}finally{//最后逻辑

}

}else{

System.out.println("======卖完啦=======");

hasGoods= false;

}

}

}).start();

i--;

}

}public voidsetGoodsLen (Integer len) {

Jedis jedis=JedisPoolConnect.connectJedis();try{

jedis.set(GOODS_LEN_KEY, String.valueOf(len));

}finally{

jedis.close();

}

}privateInteger getGoodsLen () {

Jedis jedis=JedisPoolConnect.connectJedis();try{

String val=jedis.get(GOODS_LEN_KEY);if (val != null) {returnInteger.parseInt(val);

}

}finally{

jedis.close();

}return 0;

}private voiddecrGoodsLen () {

Jedis jedis=JedisPoolConnect.connectJedis();try{//库存减1

jedis.decr(GOODS_LEN_KEY);

}finally{

jedis.close();

}

}

}

用junit测试上面的代码:

importorg.junit.Test;importorg.springframework.boot.test.context.SpringBootTest;

@SpringBootTestpublic classMarketWrongTests {/*** 测试超卖小程序*/@Testpublic void superMarket () throwsException {

MarketWrong marketWrong= newMarketWrong();//这次就卖500件吧

marketWrong.setGoodsLen(500);

marketWrong.superMarket();

Thread.sleep(60000); //卖一分钟

}

}

运行输出,每次库存都会变为负数,开了16个线程同时买东西:

//省略了几万行

现在库存为8

现在库存为8

现在库存为4

现在库存为4

现在库存为4

现在库存为4

现在库存为3

现在库存为-5现在库存为-5现在库存为-5现在库存为-5现在库存为-5现在库存为-5现在库存为-5现在库存为-5

===&

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值