首先在pom.xml中加入需要的redis依赖
org.springframework.boot
spring-boot-starter-data-redis
SpringBoot的yml配置文件下增加redis的配置:
redis:
host: 127.0.0.1
port: 6379
# password:
jedis:
pool:
max-active: 8 #连接池最大连接数(使用负值表示没有限制)
max-wait: -1ms #连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 8 #连接池中的最大空闲连接
min-idle: 5 #连接池中的最小空闲连接
注入Redis的操作类
@Autowired
private StringRedisTemplate stringRedisTemplate;
进行加锁,传入key值和value值
public boolean lock(String key,String value){
/**
* 键值不存在则新增,并返回true
* 键值存在则不改变原来的值,并返回false
*/
boolean res = stringRedisTemplate.opsForValue().setIfAbsent(key,value,10, TimeUnit.SECONDS);
return res;
}
进行解锁,传入key值,先判断需要解锁的值是否等于加锁的value值,如果是的话进行解锁
public void unlock(String key){
if (stringRedisTemplate.opsForValue().get(key).equals("testlock")){
//删除key
stringRedisTemplate.opsForValue().getOperations().delete(key);
}
}
模拟一下网上下单,先对下单之前进行加锁,防止其他线程进来下单,不然库存量就会有异常,在下单之后进行解锁,让其他线程进来下单,下面是下单的代码
package com.anytxn.cms.service.impl;
import com.anytxn.cms.common.RedisLock;
import com.anytxn.cms.service.SeckillService;
import com.anytxn.cms.util.HelpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
/**
* @author zjx
* @date 2019/12/24 19:37
*/
@Service
public class SeckillServiceImpl implements SeckillService {
private static final Logger logger = LoggerFactory.getLogger(SeckillServiceImpl.class);
@Autowired
private RedisLock redisLock;
/**
* 活动,特价,限量100000份
*/
private static Map products;//模拟商品信息表
private static Map stock;//模拟库存表
private static Map orders;//模拟下单成功用户表
static {
/**
* 模拟多个表,商品信息表,库存表,秒杀成功订单表
*/
products = new HashMap<>();
stock = new HashMap<>();
orders = new HashMap<>();
products.put("123456",100000);
stock.put("123456",100000);
}
private String queryMap(String productId){//模拟查询数据库
return "国庆活动,皮蛋特教,限量"
+products.get(productId)
+"份,还剩:"+stock.get(productId)
+"份,该商品成功下单用户数:"
+orders.size()+"人";
}
private String querySecKillProductInfo(String productId) {
return this.queryMap(productId);
}
@Override
public void orderProductMocckDiffUser(String productId) {
//加锁
if(!redisLock.lock("123456","testlock")){
logger.warn("很抱歉,人太多了,换个姿势再试试~~");
return;
}
//1.查询该商品库存,为0则活动结束
int stockNum = stock.get(productId);
if(stockNum == 0){
logger.warn("活动已经结束");
return;
}else {
//2.下单
orders.put(HelpUtil.getUniqueKey(),productId);
//3.减库存
stockNum = stockNum - 1;
try{
Thread.sleep(100);//模拟减库存的处理时间
}catch (InterruptedException e){
e.printStackTrace();
}
stock.put(productId,stockNum);
}
String result = this.querySecKillProductInfo("123456");
System.out.println(Thread.currentThread().getName() + ":" + result + ",成功下单");
//解锁
redisLock.unlock("123456");
}
}
开启多个线程进行同时下单,看库存是否有异常
package com.anytxn;
import com.anytxn.cms.CmsApp;
import com.anytxn.cms.service.SeckillService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.concurrent.*;
/**
* @author zjx
* @date 2019/12/24 20:11
*/
@SpringBootTest(classes = CmsApp.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class SeckillServiceTest {
private static final Logger logger = LoggerFactory.getLogger(SeckillServiceTest.class);
@Autowired
private SeckillService seckillService;
/**
* 开启多个线程模拟高并发,测试redis分布式锁
*/
@Test
public void test(){
//线程数量
int threadSize = 3;
ExecutorService executorService = Executors.newFixedThreadPool(threadSize);
CompletionService completionService = new ExecutorCompletionService<>(executorService);
for (int i = 0;i < threadSize;i++){
completionService.submit(new Callable() {
@Override
public Object call() throws Exception {
for (int i = 0;i < 1000; i++){
seckillService.orderProductMocckDiffUser("123456");
}
return true;
}
});
}
for (int i = 0; i < threadSize; i++){
try {
boolean result = (boolean)completionService.take().get();
}catch (Exception e){
e.printStackTrace();
}
}
//关闭线程池
executorService.shutdown();
}
}
先注入Redis操作类,直接用这个类进行存数据和取数据就可以了
package com.anytxn.cms.common;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author zjx
* @date 2019/12/25 16:02
*/
@Service
public class RedisService {
/**
* @Autowired 默认按照类型装配的
* @Resource 默认按照名字装配
* 由于RedisTemplate的默认类型是RedisTemplate,如果使用@Autowired则只能注入RedisTemplate
* 若注入RedisTemplate只能用注解 @Resource
*/
//private RedisTemplate redisTemplate;
/*@Resource
public void setRedisTemplate(RedisTemplate redisTemplate) {
RedisSerializer stringSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setValueSerializer(stringSerializer);
redisTemplate.setHashKeySerializer(stringSerializer);
redisTemplate.setHashValueSerializer(stringSerializer);
this.redisTemplate = redisTemplate;
}*/
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 存数据到redis
*/
public void pushVaule(Map mapVale, String key, long expireMinutes, TimeUnit timeUnit){
try {
redisTemplate.opsForHash().putAll(key,mapVale);
redisTemplate.expire(key,expireMinutes,timeUnit);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 从redis中取数据
*/
public Map
单元测试Redis
package com.anytxn;
import com.alibaba.fastjson.JSON;
import com.anytxn.cms.CmsApp;
import com.anytxn.cms.common.RedisService;
import com.anytxn.cms.model.CreditBill;
import com.anytxn.cms.repository.CreditBillRepository;
import com.anytxn.cms.util.DateUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
/**
* @author zjx
* @date 2019/12/25 16:35
*/
@SpringBootTest(classes = CmsApp.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class RedisTest {
@Autowired
private RedisService redisService;
@Autowired
private CreditBillRepository creditBillRepository;
/**
* 测试往redis存数据
*/
@Test
public void test(){
CreditBill creditBill = new CreditBill();
creditBill.setName("jx");
creditBill.setAccountID("888888");
creditBill.setAddress("zjx");
creditBill.setAmount(new BigDecimal("0.00"));
creditBill.setDate(DateUtils.format(new Date(),DateUtils.DATE_PATTERN));
String strCreditBill = JSON.toJSONString(creditBill);
Map map = new HashMap<>();
map.put("1",strCreditBill);
redisService.pushVaule(map,"model_key",20L, TimeUnit.MINUTES);
creditBillRepository.save(creditBill);
}
/**
* 测试往redis取数据
* 若取不到则查数据库
*/
@Test
public void test1(){
CreditBill creditBill;
Map map = redisService.getVaule("model_key");
if (!CollectionUtils.isEmpty(map)){
String strCreditBill = map.get("1").toString();
creditBill = JSON.toJavaObject(JSON.parseObject(strCreditBill),CreditBill.class);
}else {
Optional CreResult = creditBillRepository.findById(2);
creditBill = CreResult.isPresent() ? CreResult.get() : null;
}
System.out.println(creditBill.toString());
}
}