java-使用redis模拟实现抢购、秒杀
//自定义分布式锁-抢占优惠名额,确保线程安全
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping("/robQuota")
public String robQuota() {
String distributedKey = "discounts_lock";//先定义一个key
String redisClientID = UUID.randomUUID().toString();//模拟使用UUID作为value
CourseDiscounts courseDiscounts = courseDiscountsService.selectByPrimaryKey(1);//查询抢购商品信息
int timeOut = 4;//暂定锁的超时时间为4秒
//使用redis模板进行上锁(springboot整合redis提供)
Boolean lockResult = stringRedisTemplate.opsForValue().setIfAbsent(distributedKey, redisClientID, timeOut, TimeUnit.SECONDS);
//确保多个线程访问进来的时候,只有一个成功上锁的,只有一个抢购成功的
if (!lockResult) {
//这里可以使用重入锁+偏向锁的机制,当没抢到的时候,重新执行,当抢购5次都没抢到,再返回一个友好提示-当前人数较多,请稍后重新抢购
//return robQuota();
return "当前活动参与人数过多,请稍后重试!";
}
//自定义一个线程当做当前线程的守护线程,用来给当前线程没执行完,给锁加失效时间(如果当前设置锁超时时间为10秒,这个线程就是用来给锁加时间,因为业务还没执行完,不能释放锁,只有当执行完或者挂掉,确保释放锁)
GuardThread guardThread = new GuardThread(distributedKey, timeOut, stringRedisTemplate);
guardThread.setDaemon(true);//如果当前线程处理业务因为某些原因挂了,那么守护线程也跟着挂
guardThread.start();
try {
Integer discountsCount = courseDiscounts.getDiscountsCount();
//如果数量大于0,进行抢购动作
if (discountsCount > 0) {
Thread.sleep(12000);
courseDiscounts.setDiscountsCount(courseDiscounts.getDiscountsCount() - 1);
courseDiscountsService.updateByPrimaryKeySelective(courseDiscounts);
System.out.println("[端口:" + port + "],扣减一个名额,剩余名额数量:" + courseDiscounts.getDiscountsCount());
} else {
System.out.println("[端口:" + port + "],扣减失败,优惠名额不足!");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//确保锁是由当前线程释放的,再释放锁
if (stringRedisTemplate.opsForValue().get(distributedKey).equals(redisClientID)) {
stringRedisTemplate.delete(distributedKey);
}
}
return "[端口:" + port + "] - " + courseDiscounts.toString();
}
java-使用redisson模拟实现抢购、秒杀
基本和redis实现的方式差不多
@Autowired
private RedissonClient redisson;
@RequestMapping("/robQuotaRedisson")
public String robQuotaRedisson() {
String distributedKey = "redisson_discounts_lock";
RLock redissonLock = redisson.getLock(distributedKey);
CourseDiscounts courseDiscounts;
try {
redissonLock.lock(10, TimeUnit.SECONDS);
courseDiscounts = courseDiscountsService.selectByPrimaryKey(1);
if (courseDiscounts.getDiscountsCount() > 0) {
courseDiscounts.setDiscountsCount(courseDiscounts.getDiscountsCount() - 1);
int i = courseDiscountsService.updateByPrimaryKeySelective(courseDiscounts);
if (i > 0)
System.out.println("[端口: " + port + "],扣减一个名额,剩余名额数量:" + courseDiscounts.getDiscountsCount());
} else {
System.out.println("[端口:" + port + "],扣减失败,优惠名额不足!");
}
}finally {
redissonLock.unlock();
}
return "[端口:" + port + "] - " + courseDiscounts.toString();
}
自定义守护线程代码
package com.test.thread;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.concurrent.TimeUnit;
public class GuardThread extends Thread {
private String lockKey;
private int timeOut;
private StringRedisTemplate stringRedisTemplate;
public GuardThread(String lockKey, int timeOut, StringRedisTemplate stringRedisTemplate){
this.lockKey = lockKey;
this.timeOut = timeOut;
this.stringRedisTemplate = stringRedisTemplate;
}
@Override
public void run() {
while (true){
try {
Thread.sleep(timeOut / 2 * 1000);
}catch (Exception e){
e.printStackTrace();
}
//获取到锁的失效时间还有多久
Long expire = stringRedisTemplate.getExpire(lockKey, TimeUnit.SECONDS);
if (expire > 0){
stringRedisTemplate.expire(lockKey, expire + timeOut / 2, TimeUnit.SECONDS);
}
}
}
}