Bei verteilten Sperren besteht ein häufiges Problem darin, dass ein Dienst setnx erfolgreich ist, beim Entsperren jedoch eine Ausfallzeit oder einige besondere Faktoren auftreten und er nicht entsperrt werden kann. Dann geraten andere Dienste in einen Deadlock-Zustand. Daher denken wir bei der Verwendung von setnx darüber nach, den Ablaufbefehl für die Sperre zu verwenden. Aus den Anweisungen können wir ersehen, dass die Befehle „setnx“ und „expire“ getrennt sind Wenn die Anweisung nicht fortgesetzt werden kann, führt dies ebenfalls zu einem Deadlock.
Lösung:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; @Component public class RedisLock { Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private StringRedisTemplate redisTemplate; /** * 加锁 * @param key * @param value 当前时间 + 超时时间 * @return */ public boolean lock(String key, String value) { if (redisTemplate.opsForValue().setIfAbsent(key, value)) { // 这个其实就是setnx命令,只不过在java这边稍有变化,返回的是boolean // 设置个过期时间,当然如果在这中间的空隙过程中如果有特殊因素导致指令无法继续,也会导致死锁的产生,如果死锁出现,则后续代码会处理 redisTemplate.expire(key, lockTime, TimeUnit.SECONDS); return true; } // 避免死锁,且只让一个线程拿到锁 String currentValue = redisTemplate.opsForValue().get(key); // 如果锁过期了 if (!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()) { //获取上一个锁的时间 String oldValues = redisTemplate.opsForValue().getAndSet(key, value); /* 只会让一个线程拿到锁 如果旧的value和currentValue相等,只会有一个线程达成条件,因为第二个线程拿到的oldValue已经和currentValue不一样了 */ if (!StringUtils.isEmpty(oldValues) && oldValues.equals(currentValue)) { return true; } } return false; } /** * 解锁 * @param key * @param value */ 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) { logger.error("redis分布式锁解锁异常,{}", e); } } }
Anruf:
//加锁 long time = System.currentTimeMillis() + 1000 * lockTime //超时时间:10秒,最好设为常量 boolean isLock = redisLock.lock(...keyName, String.valueOf(time)); if(!isLock){ throw new RuntimeException("系统正忙"); } // doSomething... //解锁 redisLock.unlock(...keyName, String.valueOf(time));
Weitere Informationen zu Redis finden Sie in der Spalte Tutorial zur Redis-Nutzung!
Das obige ist der detaillierte Inhalt vonWird Redis Deadlock-Probleme verursachen?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!