1、依賴
<!--redisson分布式鎖-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.5.0</version>
</dependency>
2、工具類
package com.hsshy.beam.distributedlock.redis;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;
/**
* redis分布式鎖幫助類
* @author 科幫網 By https://blog.52itstyle.com
*/
public class RedissLockUtil {
private static RedissonClient redissonClient;
public void setRedissonClient(RedissonClient locker) {
redissonClient = locker;
}
/**
* 加鎖
* @param lockKey
* @return
*/
public static RLock lock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock();
return lock;
}
/**
* 釋放鎖
* @param lockKey
*/
public static void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.unlock();
}
/**
* 釋放鎖
* @param lock
*/
public static void unlock(RLock lock) {
lock.unlock();
}
/**
* 帶逾時的鎖
* @param lockKey
* @param timeout 逾時時間 機關:秒
*/
public static RLock lock(String lockKey, int timeout) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, TimeUnit.SECONDS);
return lock;
}
/**
* 帶逾時的鎖
* @param lockKey
* @param unit 時間機關
* @param timeout 逾時時間
*/
public static RLock lock(String lockKey, TimeUnit unit , int timeout) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, unit);
return lock;
}
/**
* 嘗試擷取鎖
* @param lockKey
* @param waitTime 最多等待時間
* @param leaseTime 上鎖後自動釋放鎖時間
* @return
*/
public static boolean tryLock(String lockKey, int waitTime, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
} catch (InterruptedException e) {
return false;
}
}
/**
* 嘗試擷取鎖
* @param lockKey
* @param unit 時間機關
* @param waitTime 最多等待時間
* @param leaseTime 上鎖後自動釋放鎖時間
* @return
*/
public static boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, unit);
} catch (InterruptedException e) {
return false;
}
}
}
3、redisson屬性裝配類
package com.hsshy.beam.distributedlock.redis;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "redisson")
public class RedissonProperties {
private int timeout = 3000;
private String address;
private String password;
private int connectionPoolSize = 64;
private int connectionMinimumIdleSize=10;
private int slaveConnectionPoolSize = 250;
private int masterConnectionPoolSize = 250;
private String[] sentinelAddresses;
private String masterName;
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public int getSlaveConnectionPoolSize() {
return slaveConnectionPoolSize;
}
public void setSlaveConnectionPoolSize(int slaveConnectionPoolSize) {
this.slaveConnectionPoolSize = slaveConnectionPoolSize;
}
public int getMasterConnectionPoolSize() {
return masterConnectionPoolSize;
}
public void setMasterConnectionPoolSize(int masterConnectionPoolSize) {
this.masterConnectionPoolSize = masterConnectionPoolSize;
}
public String[] getSentinelAddresses() {
return sentinelAddresses;
}
public void setSentinelAddresses(String sentinelAddresses) {
this.sentinelAddresses = sentinelAddresses.split(",");
}
public String getMasterName() {
return masterName;
}
public void setMasterName(String masterName) {
this.masterName = masterName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getConnectionPoolSize() {
return connectionPoolSize;
}
public void setConnectionPoolSize(int connectionPoolSize) {
this.connectionPoolSize = connectionPoolSize;
}
public int getConnectionMinimumIdleSize() {
return connectionMinimumIdleSize;
}
public void setConnectionMinimumIdleSize(int connectionMinimumIdleSize) {
this.connectionMinimumIdleSize = connectionMinimumIdleSize;
}
}
4、SpringBoot自動裝配類
package com.hsshy.beam.distributedlock.redis;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//import org.redisson.config.SentinelServersConfig;
@Configuration
@ConditionalOnClass(Config.class)
@EnableConfigurationProperties(RedissonProperties.class)
public class RedissonAutoConfiguration {
@Autowired
private RedissonProperties redssionProperties;
/**
* 哨兵模式自動裝配
* @return
*/
// @Bean
// @ConditionalOnProperty(name="redisson.master-name")
// RedissonClient redissonSentinel() {
// Config config = new Config();
// SentinelServersConfig serverConfig = config.useSentinelServers().addSentinelAddress(redssionProperties.getSentinelAddresses())
// .setMasterName(redssionProperties.getMasterName())
// .setTimeout(redssionProperties.getTimeout())
// .setMasterConnectionPoolSize(redssionProperties.getMasterConnectionPoolSize())
// .setSlaveConnectionPoolSize(redssionProperties.getSlaveConnectionPoolSize());
//
// if(StringUtils.isNotBlank(redssionProperties.getPassword())) {
// serverConfig.setPassword(redssionProperties.getPassword());
// }
// return Redisson.create(config);
// }
/**
* 單機模式自動裝配
* @return
*/
@Bean
@ConditionalOnProperty(name="redisson.address")
RedissonClient redissonSingle() {
Config config = new Config();
SingleServerConfig serverConfig = config.useSingleServer()
.setAddress(redssionProperties.getAddress())
.setTimeout(redssionProperties.getTimeout())
.setConnectionPoolSize(redssionProperties.getConnectionPoolSize())
.setConnectionMinimumIdleSize(redssionProperties.getConnectionMinimumIdleSize());
if(StringUtils.isNotBlank(redssionProperties.getPassword())) {
serverConfig.setPassword(redssionProperties.getPassword());
}
return Redisson.create(config);
}
/**
* 裝配locker類,并将執行個體注入到RedissLockUtil中
* @return
*/
@Bean
RedissLockUtil redissLockUtil(RedissonClient redissonClient) {
RedissLockUtil redissLockUtil = new RedissLockUtil();
redissLockUtil.setRedissonClient(redissonClient);
return redissLockUtil;
}
}
5、配置檔案application.yml 加上
# 哨兵模式
#redisson:
# master-name: mymaster
# password:
# sentinel-addresses: 10.47.91.83:26379,10.47.91.83:26380,10.47.91.83:26381
redisson:
address: redis://127.0.0.1:6379
password:
6、使用1(會出現超賣現象)
@Override
@Transactional
public R startSeckilRedisLock(long seckillId, long userId) {
boolean res=false;
try {
res = RedissLockUtil.tryLock(seckillId+"", TimeUnit.SECONDS, 3, 20);
if(res){
Seckill seckill = seckillService.selectById(seckillId);
if(seckill.getNumber()>0){
SuccessKilled killed = new SuccessKilled();
killed.setSeckillId(seckillId);
killed.setUserId(userId);
killed.setState(0);
killed.setCreateTime(new Timestamp(new Date().getTime()));
successKilledService.save(killed);
Seckill temp = new Seckill();
temp.setId(seckillId);
temp.setNumber(0);
seckillMapper.updateNum(temp);
}else{
return R.fail();
}
}else{
return R.fail();
}
} catch (Exception e) {
e.printStackTrace();
} finally{
if(res){//釋放鎖
RedissLockUtil.unlock(seckillId+"");
}
}
return R.ok();
}
7、使用2 (使用aop注解)
package com.hsshy.beam.aop;
import java.lang.annotation.*;
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisServicelock {
String description() default "";
}
package com.hsshy.beam.aop;
import com.hsshy.beam.distributedlock.redis.RedissLockUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.Scope;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 同步鎖 AOP
* 建立者 張志朋
* 建立時間 2015年6月3日
* @transaction 中 order 大小的說明
* https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/htmlsingle/#transaction-declarative-annotations
* https://docs.spring.io/spring/docs/4.3.14.RELEASE/javadoc-api/
*/
@Component
@Scope
@Aspect
@Order(1)
//order越小越是最先執行,但更重要的是最先執行的最後結束。order預設值是2147483647
public class RedisLockAspect {
/**
* 思考:為什麼不用synchronized
* service 預設是單例的,并發下lock隻有一個執行個體
*/
private static Lock lock = new ReentrantLock(true);//互斥鎖 參數預設false,不公平鎖
//Service層切點 用于記錄錯誤日志
@Pointcut("@annotation(com.hsshy.beam.aop.RedisServicelock)")
public void lockAspect() {
}
@Around("lockAspect()")
public Object around(ProceedingJoinPoint joinPoint) {
boolean res=false;
Object obj = null;
String seckillId = joinPoint.getArgs()[0].toString();
try {
res = RedissLockUtil.tryLock(seckillId, TimeUnit.SECONDS, 3, 20);
if(res){
obj = joinPoint.proceed();
}
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException();
} finally{
if(res){//釋放鎖
RedissLockUtil.unlock(seckillId+"");
}
}
return obj;
}
}