對於這個問題我們首先想到的是使用synchronized關鍵字,這是一種解決方法,例如:
public synchronized void setStatus(boolean b) {}
但synchronized不適合高併發資料,高併發資料會導致程式特別慢,也不適合叢集,負載均衡後資料就會出現問題。
我們使用另一種解決方法redis分散式鎖,redis為單執行緒服務的,高效的key/value結構,支援高可用的分散式叢集,能夠做到多臺機器上多個程序對同一資料互斥。
我們講一下redis從建立到使用的過程:
1、使用maven下載redis依賴的包,在pom.xml中配置:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>1.5.14.RELEASE</version></dependency>
2、熟悉redis的指令,redis的中文網址:http://www.redis.cn,我們主要熟悉下setnx指令:設定value值設定成功返回1,當有值時返回0;另一個需要熟悉的指令是getset:先取得以前的值,再設定新值。
3、新建redis類寫加鎖、解鎖的方法:
// 自動注入redis
@Autowired
private StringRedisTemplate redisTemplate;
/***
*加鎖
* @param key id
* @param value 當前時間+超時時間
* @return*/
public boolean lock(String key, String value) { // 對應命令的setnx
// 可以設定返回true,不可以設定即有值返回false
if(redisTemplate.opsForValue().setIfAbsent(key, value)) { return true; }
// 保證其中只有一個執行緒拿到鎖
String currentValue = redisTemplate.opsForValue().get(key);
// 如果鎖過期,儲存進去的鎖小於當前時間
if (!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()) {
// 獲取上一個鎖的時間
String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) { return true; } } return false;}
/**
*解鎖
*/public void unlock(String key, String value) {
try {
String currentValue = redisTemplate.opsForValue().get(key); if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
redisTemplate.opsForValue().getOperations().delete(key); } } catch(Exception e) { log.error(【redis分散式鎖】解鎖異常, {}",e); }
4、呼叫
private static final int TIMEOUT = 10 * 1000;// 超時時間10s
private RedisLock redisLock;
// 加鎖
long time = System.currentTimeMillis() + TIMEOUT;
String id = "123";
if (!redisLock.lock(id, String.valueOf(time))){
throw new XXXException(101, “請求過多請稍後”);}
// 中間是需要執行的方法
// 解鎖
redisLock.unlock(id, String.valueOf(time));
總結:當前id為空或失效的情況下返回true即可以加鎖;當前id不為空或沒失效的情況下返回false即不能加鎖已有程序在處理。
對於這個問題我們首先想到的是使用synchronized關鍵字,這是一種解決方法,例如:
public synchronized void setStatus(boolean b) {}
但synchronized不適合高併發資料,高併發資料會導致程式特別慢,也不適合叢集,負載均衡後資料就會出現問題。
我們使用另一種解決方法redis分散式鎖,redis為單執行緒服務的,高效的key/value結構,支援高可用的分散式叢集,能夠做到多臺機器上多個程序對同一資料互斥。
我們講一下redis從建立到使用的過程:
1、使用maven下載redis依賴的包,在pom.xml中配置:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>1.5.14.RELEASE</version></dependency>
2、熟悉redis的指令,redis的中文網址:http://www.redis.cn,我們主要熟悉下setnx指令:設定value值設定成功返回1,當有值時返回0;另一個需要熟悉的指令是getset:先取得以前的值,再設定新值。
3、新建redis類寫加鎖、解鎖的方法:
// 自動注入redis
@Autowired
private StringRedisTemplate redisTemplate;
/***
*加鎖
* @param key id
* @param value 當前時間+超時時間
* @return*/
public boolean lock(String key, String value) { // 對應命令的setnx
// 可以設定返回true,不可以設定即有值返回false
if(redisTemplate.opsForValue().setIfAbsent(key, value)) { return true; }
// 保證其中只有一個執行緒拿到鎖
String currentValue = redisTemplate.opsForValue().get(key);
// 如果鎖過期,儲存進去的鎖小於當前時間
if (!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()) {
// 獲取上一個鎖的時間
String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) { return true; } } return false;}
/**
*解鎖
*/public void unlock(String key, String value) {
try {
String currentValue = redisTemplate.opsForValue().get(key); if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
redisTemplate.opsForValue().getOperations().delete(key); } } catch(Exception e) { log.error(【redis分散式鎖】解鎖異常, {}",e); }
4、呼叫
private static final int TIMEOUT = 10 * 1000;// 超時時間10s
@Autowired
private RedisLock redisLock;
// 加鎖
long time = System.currentTimeMillis() + TIMEOUT;
String id = "123";
if (!redisLock.lock(id, String.valueOf(time))){
throw new XXXException(101, “請求過多請稍後”);}
// 中間是需要執行的方法
// 解鎖
redisLock.unlock(id, String.valueOf(time));
總結:當前id為空或失效的情況下返回true即可以加鎖;當前id不為空或沒失效的情況下返回false即不能加鎖已有程序在處理。