package com.campuscard.core.utils; import java.util.Date; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.serializer.RedisSerializer; public class RedisLock { // 加锁超时时间,单位毫秒, 即:加锁时间内执行完操作,如果未完成会有并发现象 private static final long LOCK_TIMEOUT = 3 * 1000; private StringRedisTemplate stringRedisTemplate; public RedisLock(StringRedisTemplate stringRedisTemplate) { this.stringRedisTemplate = stringRedisTemplate; } /** * 加锁 取到锁加锁,取不到锁一直等待知道获得锁 * * @param lockKey * @param threadName * @return */ public synchronized long lock(String lockKey) { while (true) { // 循环获取锁 // 锁时间 Long lock_timeout = currtTimeForRedis() + LOCK_TIMEOUT + 1; if (stringRedisTemplate.execute(new RedisCallback<Boolean>() { @Override public Boolean doInRedis(RedisConnection redisConnection) throws DataAccessException { // 定义序列化方式 RedisSerializer<String> serializer = stringRedisTemplate.getStringSerializer(); byte[] value = serializer.serialize(lock_timeout.toString()); boolean flag = redisConnection.setNX(lockKey.getBytes(), value); return flag; } })) { // 如果加锁成功 // 设置超时时间,释放内存 stringRedisTemplate.expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS); return lock_timeout; } else { // 获取redis里面的时间 String result = stringRedisTemplate.opsForValue().get(lockKey); Long currt_lock_timeout_str = result == null ? null : Long.parseLong(result); long time = new Date().getTime(); // 锁已经失效 if (currt_lock_timeout_str != null && currt_lock_timeout_str < time) { // 判断是否为空,不为空时,说明已经失效,如果被其他线程设置了值,则第二个条件判断无法执行 // 获取上一个锁到期时间,并设置现在的锁到期时间 String str = stringRedisTemplate.opsForValue().getAndSet(lockKey, lock_timeout.toString()); if(StringUtils.isNotBlank(str)) { Long old_lock_timeout_Str = Long.parseLong(str); if (old_lock_timeout_Str != null && old_lock_timeout_Str.equals(currt_lock_timeout_str)) { // 多线程运行时,多个线程签好都到了这里,但只有一个线程的设置值和当前值相同,它才有权利获取锁 // 设置超时间,释放内存 stringRedisTemplate.expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS); // 返回加锁时间 return lock_timeout; } } } } try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * 解锁 * * @param lockKey * @param lockValue * @param threadName */ public synchronized void unlock(String lockKey, long lockValue) { // 获取redis中设置的时间 String result = stringRedisTemplate.opsForValue().get(lockKey); Long currt_lock_timeout_str = result == null ? null : Long.parseLong(result); // 如果是加锁者,则删除锁, 如果不是,则等待自动过期,重新竞争加锁 if (currt_lock_timeout_str != null && currt_lock_timeout_str == lockValue) { stringRedisTemplate.delete(lockKey); } } /** * 多服务器集群,使用下面的方法,代替System.currentTimeMillis(),获取redis时间,避免多服务的时间不一致问题!!! * * @return */ public long currtTimeForRedis() { return stringRedisTemplate.execute(new RedisCallback<Long>() { @Override public Long doInRedis(RedisConnection redisConnection) throws DataAccessException { return redisConnection.time(); } }); } }