依赖
<!-- RedisTemplate -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Redis-Jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
application.yaml配置文件
spring:
redis:
host: 127.0.0.1
port: 6379
database: 0
timeout: 4000
jedis:
pool:
max-wait: -1
max-active: -1
max-idle: 20
min-idle: 10
public class ObjectMapperConfig {
public static final ObjectMapper objectMapper;
private static final String PATTERN = "yyyy-MM-dd HH:mm:ss";
static {
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
objectMapper = new ObjectMapper()
// 转换为格式化的json(控制台打印时,自动格式化规范)
//.enable(SerializationFeature.INDENT_OUTPUT)
// Include.ALWAYS 是序列化对像所有属性(默认)
// Include.NON_NULL 只有不为null的字段才被序列化,属性为NULL 不序列化
// Include.NON_EMPTY 如果为null或者 空字符串和空集合都不会被序列化
// Include.NON_DEFAULT 属性为默认值不序列化
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
// 如果是空对象的时候,不抛异常
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
// 反序列化的时候如果多了其他属性,不抛出异常
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
// 取消时间的转化格式,默认是时间戳,可以取消,同时需要设置要表现的时间格式
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.setDateFormat(new SimpleDateFormat(PATTERN))
// 对LocalDateTime序列化跟反序列化
.registerModule(javaTimeModule)
.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY)
// 此项必须配置,否则会报java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY)
;
}
static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
@Override
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(value.format(DateTimeFormatter.ofPattern(PATTERN)));
}
}
static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext deserializationContext) throws IOException {
return LocalDateTime.parse(p.getValueAsString(), DateTimeFormatter.ofPattern(PATTERN));
}
}
}
@Configuration
public class RedisConfig {
/**
* redisTemplate配置
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 配置连接工厂
template.setConnectionFactory(factory);
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
Jackson2JsonRedisSerializer<Object> jacksonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
jacksonSerializer.setObjectMapper(ObjectMapperConfig.objectMapper);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// 使用StringRedisSerializer来序列化和反序列化redis的key,value采用json序列化
template.setKeySerializer(stringRedisSerializer);
template.setValueSerializer(jacksonSerializer);
// 设置hash key 和value序列化模式
template.setHashKeySerializer(stringRedisSerializer);
template.setHashValueSerializer(jacksonSerializer);
template.afterPropertiesSet();
return template;
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Accessors(chain = true)
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
/**
* 商品名
*/
private String name;
/**
* 库存
*/
private Integer leftNum;
}
public interface ProductService {
Product getById(Integer id);
void updateById(Product product);
List<Product> list();
}
@Service
public class ProductServiceImpl implements ProductService {
private static final Map<Integer, Product> productMap;
static {
productMap = new HashMap<>();
productMap.put(productMap.size() + 1, Product.builder().id(productMap.size() + 1).name("苹果").leftNum(10).build());
}
@Override
public Product getById(Integer id) {
return productMap.get(id);
}
@Override
public void updateById(Product product) {
Product update = productMap.get(product.getId());
update.setLeftNum(product.getLeftNum());
productMap.put(product.getId(), update);
}
@Override
public List<Product> list() {
return new ArrayList<>(productMap.values());
}
}
@Slf4j
@Component
public class RedisLock {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ProductService productService;
private static final int TIMEOUT = 4000;
private static final String LOCK_PREFIX = "secKill:";
private final AtomicInteger i = new AtomicInteger(1);
public void secKill(int productId) {
// 查询库存
Product product = productService.getById(1);
if (product.getLeftNum() < 1) {
log.error("库存不足");
return;
}
// 加锁
String uuid = UUID.randomUUID().toString();
if (!lock(LOCK_PREFIX + productId, uuid)) {
log.info("活动太火爆了,请稍后再操作");
return;
}
//秒杀逻辑
try {
product.setLeftNum(product.getLeftNum() - 1);
productService.updateById(product);
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 解锁
unlock(LOCK_PREFIX + productId, uuid);
log.info("秒杀成功" + i.getAndIncrement());
}
/**
* 加锁
*/
public boolean lock(String key, String value) {
Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(key, value, Duration.ofMillis(TIMEOUT));
if (ifAbsent != null && ifAbsent) {
log.info("加锁成功");
return true;
}
return false;
}
/**
* 解锁(lua脚本原子性)
*/
public void unlock(String key, String value) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] " +
"then return redis.call('del', KEYS[1]) " +
"else return 0 " +
"end";
List<String> keys = new ArrayList<>();
keys.add(key);
Long execute = redisTemplate.execute(RedisScript.of(script, Long.class), keys, value);
log.info("解锁成功 = " + execute);
}
}
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@Autowired
private RedisLock redisLock1;
@GetMapping("/list")
public List<Product> list() {
return productService.list();
}
@GetMapping("/inr")
public void inrLeft(Integer id) {
productService.updateById(Product.builder().id(id).leftNum(10).build());
}
@GetMapping("/buy")
public String buy(long time) {
for (int i = 0; i < 100; i++) {
try {
new Thread(() -> redisLock1.secKill(1)).start();
Thread.sleep(time);
} catch (Exception e) {
e.printStackTrace();
}
}
return "秒杀结束";
}
}
http://localhost:8080/list
http://localhost:8080/buy?time=50
http://localhost:8080/list
@Slf4j
@Component
public class RedisLock2 {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ProductService productService;
private static final int TIMEOUT = 4000;
private static final String LOCK_PREFIX = "secKill:";
private final AtomicInteger i = new AtomicInteger(1);
private static final ThreadLocal<String> local = new ThreadLocal<>();
public void secKill(int productId) {
Product product = productService.getById(1);
if (product.getLeftNum() < 1) {
log.error("库存不足");
return;
}
//加锁
String uuid = UUID.randomUUID().toString();
if (!lock(LOCK_PREFIX + productId, uuid)) {
log.info("活动太火爆了,请稍后再操作");
return;
}
//秒杀逻辑
try {
product.setLeftNum(product.getLeftNum() - 1);
productService.updateById(product);
Thread.sleep(80);
} catch (InterruptedException e) {
e.printStackTrace();
}
//解锁
unlock(LOCK_PREFIX + productId, uuid);
log.info("秒杀成功" + i.getAndIncrement());
}
/**
* 加锁
*/
public boolean lock(String key, String value) {
Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(key, value, Duration.ofMillis(TIMEOUT));
if (ifAbsent != null && ifAbsent) {
log.info("加锁成功");
local.set(value);
return true;
}
return false;
}
/**
* 解锁
*/
public void unlock(String key, String value) {
String localValue = local.get();
if (localValue.equals(value) && localValue.equals(redisTemplate.opsForValue().get(key))) {
log.info("解锁成功");
redisTemplate.delete(key);
local.remove();
}
}
}
@GetMapping("/buy2")
public String buy2(long time) {
for (int i = 0; i < 100; i++) {
try {
new Thread(() -> redisLock2.secKill(1)).start();
Thread.sleep(time);
} catch (Exception e) {
e.printStackTrace();
}
}
return "秒杀结束";
}
@Slf4j
@Component
public class RedisLock3 {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ProductService productService;
private static final int TIMEOUT = 4000;
private static final String LOCK_PREFIX = "secKill:";
private final AtomicInteger ato = new AtomicInteger(1);
public void secKill(int productId) {
Product product = productService.getById(1);
if (product.getLeftNum() < 1) {
log.error("库存不足");
return;
}
//加锁
//value包含拥有者标识,具有唯一性,防止任何人都可以解锁
String userId = UUID.randomUUID() + ":" + System.currentTimeMillis() + TIMEOUT;
if (!lock(LOCK_PREFIX + productId, userId)) {
log.error("活动太火爆了,请稍后再操作");
return;
}
//秒杀逻辑
try {
product.setLeftNum(product.getLeftNum() - 1);
productService.updateById(product);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//解锁
unlock(LOCK_PREFIX + productId, userId);
log.info("秒杀成功" + ato.getAndIncrement());
}
/**
* 加锁
*/
public boolean lock(String key, String value) {
//没有锁,持有并加锁
Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(key, value);
if (ifAbsent != null && ifAbsent) {
log.info("加锁成功");
return true;
}
//锁已被持有,判断锁过期
synchronized (RedisLock3.class) {
String redisValue = (String) redisTemplate.opsForValue().get(key);
if (StringUtils.hasLength(redisValue) && Long.parseLong(redisValue.split(":")[1]) < System.currentTimeMillis()) {
String oldValue = (String) redisTemplate.opsForValue().getAndSet(key, value);
if (StringUtils.hasLength(oldValue) && oldValue.equals(redisValue)) {
log.info("锁过期,重新持有锁");
return true;
}
}
}
return false;
}
/**
* 解锁
*/
public void unlock(String key, String value) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] " +
"then return redis.call('del', KEYS[1]) " +
"else return 0 " +
"end";
List<String> keys = new ArrayList<>();
keys.add(key);
Long execute = redisTemplate.execute(RedisScript.of(script, Long.class), keys, value);
System.out.println("解锁成功 = " + execute);
}
}
@GetMapping("/buy3")
public String buy3(long time) {
for (int i = 0; i < 100; i++) {
try {
new Thread(() -> redisLock3.secKill(1)).start();
Thread.sleep(time);
} catch (Exception e) {
e.printStackTrace();
}
}
return "秒杀结束";
}
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/weixin_43296313/article/details/125446488
内容来源于网络,如有侵权,请联系作者删除!