使用jedis写分布式锁,getRedisLock:获取锁 releaseRedisLock:释放锁
package com.gaia.test.redisTest.distributelock;
import com.gaia.test.redisTest.util.JedisPoolInstance;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.util.UUID;
/**
* Description:
*
* @author xuzhiqiang
* Date 2020/12/15
*/
public class JedisDistributeLock {
private static final String redisLockPrefix = "redis:lock:";
/**
* 获取锁
*
* @param lockName
* @param acquireTimeOut 单位是毫秒
* @param lockTimeOut 单位是毫秒
* @return
*/
public String getRedisLock(String lockName, Long acquireTimeOut, Long lockTimeOut) {
String redisLockKey = redisLockPrefix + lockName;
String uniqueValue = UUID.randomUUID().toString();
JedisPool jedisPool = JedisPoolInstance.getJedisPoolInstance();
Jedis jedis = jedisPool.getResource();
System.out.println("获取jedis连接池:" + Thread.currentThread().getName() + ":" + jedisPool + ", " + jedisPool.getNumActive() + ", " + jedisPool.getNumIdle());
try {
//往后延acquireTimeOut秒(比如3秒)
Long endTime = System.currentTimeMillis() + acquireTimeOut;
//时间没有超过,有资格获取锁
while (System.currentTimeMillis() < endTime) {
//设置key 和 设置key的过期时间 不是原子操作(不在一个步骤中,是分了两步)
if (jedis.setnx(redisLockKey, uniqueValue) == 1) {
//设置key成功,表示拿到锁
jedis.pexpire(redisLockKey, lockTimeOut);
return uniqueValue;
}
//ttl检测过期时间 如果没有过期时间,重新设置
if (jedis.ttl(redisLockKey) == -1) {
//设置过期时间
jedis.pexpire(redisLockKey, lockTimeOut);
}
//立刻马上去循环再获取锁,其实不是很好,最好是稍等片刻再去重试获取锁
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
if (jedis != null) {
jedis.close();
System.out.println("关闭jedis连接池:" + Thread.currentThread().getName() + ":" + jedisPool + ", " + jedisPool.getNumActive() + ", " + jedisPool.getNumIdle());
}
}
return null;
}
/**
* 释放redis锁
*
* @param lockName
* @param uniqueValue
*/
public void releaseRedisLock(String lockName, String uniqueValue) {
//redis锁的key
String redisLockKey = redisLockPrefix + lockName;
Jedis jedis = JedisPoolInstance.getJedisPoolInstance().getResource();
try {
//自己的锁自己解,不要把别人的锁给解了
if (jedis.get(redisLockKey).equals(uniqueValue)) {
jedis.del(redisLockKey);
}
} finally {
if (jedis != null) {
jedis.close();
}
}
}
}
redis连接池 JedisPool
package com.gaia.test.redisTest.util;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* Description:
*
* @author xuzhiqiang
* Date 2020/12/15
*/
public class JedisPoolInstance {
//redis服务器的ip地址
private static final String HOST = "127.0.0.1";
//redis服务器的端口
private static final int PORT = 6379;
// private static final String PASSWORD = "123456";
private static final int TIMEOUT = 10000;
//redis连接池对象,单例的连接池对象
private static JedisPool jedisPool = null;
//私有构造方法
private JedisPoolInstance() {
}
/**
* 获取线程池实例对象
*
* @return
*/
public static JedisPool getJedisPoolInstance() {
//双重检测锁
if (null == jedisPool) {
synchronized (JedisPoolInstance.class) {
if (null == jedisPool) {
//对连接池的参数进行配置,根据项目的实际情况配置这些参数
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(1000);//最大连接数
poolConfig.setMaxIdle(32);//最大空闲连接数
poolConfig.setMaxWaitMillis(90*1000);//获取连接时的最大等待毫秒数
poolConfig.setTestOnBorrow(true);//在获取连接的时候检查连接有效性
jedisPool = new JedisPool(poolConfig, HOST, PORT, TIMEOUT);
}
}
}
return jedisPool;
}
}
业务中使用
package com.gaia.test.redisTest.business.service.impl;
import com.gaia.test.redisTest.business.mapper.GoodsMapper;
import com.gaia.test.redisTest.business.model.Goods;
import com.gaia.test.redisTest.business.service.GoodsService;
import com.gaia.test.redisTest.distributelock.JedisDistributeLock;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Description:
*
* @author xuzhiqiang
* Date 2020/12/17
*/
public class GoodsServiceImpl implements GoodsService {
@Autowired
private GoodsMapper goodsMapper;
private JedisDistributeLock jedisDistributeLock = new JedisDistributeLock();
/**
* 采用Redis分布式锁解决库存超卖问题
*
* @param id
* @return
*/
public int updateByPrimaryKeyStore(Integer id) {
String lockName = "updateByPrimaryKeyStore";
String lockUniqueValue = null;
int update = 0;
try {
//TODO 加锁,然后下面的业务代码就会按顺序排队执行
lockUniqueValue = jedisDistributeLock.getRedisLock(lockName, 3000L, 30000L);
//拿到redis分布式锁
if (lockUniqueValue != null) {
//TODO ======以下是业务代码 =====
Goods goods = goodsMapper.selectByPrimaryKey(id);
System.out.println("库存:" + goods.getStore());
//判断库存是否大于0
if (goods.getStore() > 0) {
//库存大于0,可以减库存,排它锁
update = goodsMapper.updateByPrimaryKeyStore(id);
if (update > 0) {
System.out.println("减库存成功,可以下订单");
} else {
System.out.println("减库存失败,不能下订单");
throw new RuntimeException();
}
} else {
System.out.println("没有库存");
}
//TODO ======以上是业务代码 =====
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
//TODO 解锁,释放锁
//lock.unLock();
jedisDistributeLock.releaseRedisLock(lockName, lockUniqueValue);
}
//返回结果
return update;
//目标方法执行完了,spring aop 在此处进行事务提交
}
}
测试
package com.gaia.test.redisTest.distributelock;
import com.gaia.test.redisTest.business.service.GoodsService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* Description:
*
* @author xuzhiqiang
* Date 2020/12/17
*/
public class Test {
//倒计算器
CountDownLatch countDownLatch = new CountDownLatch(1);
public static void main(String[] args) {
Test test = new Test();
test.runThread();
}
/**
* 多线程并发执行
*/
private void runThread() {
//spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
GoodsService goodsService = context.getBean(GoodsService.class);
//创建一个确定的线程池
ExecutorService executorService = Executors.newFixedThreadPool(16);
for (int i=0; i<50; i++) {
//提交线程到线程池去执行
executorService.submit(new Runnable() {
@Override
public void run() {
try {
//等待,线程就位,但是不运行
countDownLatch.await();
System.out.println("Thread:"+Thread.currentThread().getName() + ", time: "+System.currentTimeMillis());
try {
//TODO 执行业务代码 (超卖测试)
goodsService.updateByPrimaryKeyStore(1);
} catch (Throwable e) {
System.out.println("Thread:"+Thread.currentThread().getName() + e.getMessage());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
//倒计算器 -1,那么16个线程就同时开始执行,那么就达到并发效果
countDownLatch.countDown();
try {
// 传达完毕信号,等任务执行完才关闭
executorService.shutdown();
// (所有的任务都结束的时候,返回TRUE)
if(!executorService.awaitTermination(60000, TimeUnit.MILLISECONDS)){
// 超时的时候向线程池中所有的线程发出中断(interrupted),立刻马上关闭
executorService.shutdownNow();
}
} catch (InterruptedException e) {
// awaitTermination方法被中断的时候也中止线程池中全部的线程的执行。
System.out.println("awaitTermination interrupted: " + e);
executorService.shutdownNow();
}
}
}