1、分布式锁
在实际项目中,建议直接使用 Redisson,它提供了完善的分布式锁实现(包括可重入、公平锁、红锁等):
RLock lock = redisson.getLock("myLock");
lock.lock(); // 自动续期(watchdog)
try {
// 业务逻辑
} finally {
lock.unlock();
}
Java 分布式锁实现
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.Collections;
import java.util.UUID;
public class RedisDistributedLock {
private final JedisPool jedisPool;
private final String lockKey;
private final int expireTimeMs; // 锁自动过期时间(毫秒)
private String lockValue; // 当前客户端持有的唯一标识
private static final String LOCK_SUCCESS = "OK";
private static final Long RELEASE_SUCCESS = 1L;
// Lua 脚本:只有 value 匹配才删除锁(原子操作)
private static final String UNLOCK_SCRIPT =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
public RedisDistributedLock(JedisPool jedisPool, String lockKey, int expireTimeMs) {
this.jedisPool = jedisPool;
this.lockKey = lockKey;
this.expireTimeMs = expireTimeMs;
}
/**
* 尝试获取锁(非阻塞)
*/
public boolean tryLock() {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
lockValue = UUID.randomUUID().toString();
// SET key value NX PX expireTime
String result = jedis.set(lockKey, lockValue, "NX", "PX", expireTimeMs);
return LOCK_SUCCESS.equals(result);
} catch (Exception e) {
// 日志记录异常(此处简化)
e.printStackTrace();
return false;
} finally {
if (jedis != null) {
jedis.close();
}
}
}
/**
* 阻塞式获取锁,带超时时间(单位:毫秒)
*/
public boolean lock(long timeoutMs) {
long start = System.currentTimeMillis();
while (true) {
if (tryLock()) {
return true;
}
// 等待一段时间再重试
try {
Thread.sleep(20); // 20ms 轮询
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
// 检查是否超时
if (System.currentTimeMillis() - start > timeoutMs) {
return false;
}
}
}
/**
* 释放锁(必须由持有者调用)
*/
public boolean unlock() {
if (lockValue == null) {
return false;
}
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
Object result = jedis.eval(UNLOCK_SCRIPT, Collections.singletonList(lockKey),
Collections.singletonList(lockValue));
return RELEASE_SUCCESS.equals(result);
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
if (jedis != null) {
jedis.close();
}
lockValue = null; // 清空标识
}
}
// ===== 可选:支持 try-with-resources 的方式使用锁 =====
public AutoCloseableLock autoLock(long timeoutMs) {
return new AutoCloseableLock(timeoutMs);
}
public class AutoCloseableLock implements AutoCloseable {
private final boolean locked;
public AutoCloseableLock(long timeoutMs) {
this.locked = lock(timeoutMs);
if (!locked) {
throw new RuntimeException("Failed to acquire lock: " + lockKey);
}
}
@Override
public void close() {
if (locked) {
unlock();
}
}
}
}
使用示例
public class DistributedLockExample {
public static void main(String[] args) {
// 配置 Jedis 连接池(假设 Redis 在本地)
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(10);
JedisPool jedisPool = new JedisPool(poolConfig, "localhost", 6379);
String lockKey = "order:process:123";
RedisDistributedLock lock = new RedisDistributedLock(jedisPool, lockKey, 10000); // 10秒过期
// 方式一:手动加锁/解锁
if (lock.lock(5000)) { // 最多等待5秒
try {
System.out.println("获取锁成功,执行业务逻辑...");
Thread.sleep(2000); // 模拟耗时操作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
System.out.println("锁已释放");
}
} else {
System.out.println("未能在5秒内获取锁");
}
// 方式二:使用 try-with-resources(推荐)
try (RedisDistributedLock.AutoCloseableLock autoLock = lock.autoLock(5000)) {
System.out.println("进入临界区(自动管理锁)");
Thread.sleep(1000);
} catch (Exception e) {
System.err.println("加锁失败或业务异常: " + e.getMessage());
}
// 关闭连接池
jedisPool.close();
}
}
2、缓存
以下例子基于 Spring Boot + RedisTemplate
配置 RedisTemplate(支持 JSON 序列化)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// 使用 String 序列化 key
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
// 使用 JSON 序列化 value(支持复杂对象)
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
template.afterPropertiesSet();
return template;
}
}
缓存服务类(核心逻辑)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class UserService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private UserRepository userRepository; // 假设这是你的数据库访问层
private static final String USER_CACHE_PREFIX = "user:";
/**
* 根据 ID 查询用户(带缓存)
*/
public User getUserById(Long id) {
if (id == null) return null;
String cacheKey = USER_CACHE_PREFIX + id;
// 1. 先查缓存
User user = (User) redisTemplate.opsForValue().get(cacheKey);
if (user != null) {
System.out.println("命中缓存: " + cacheKey);
return user;
}
// 2. 缓存未命中,查数据库
user = userRepository.findById(id);
if (user != null) {
// 3. 写入缓存(设置过期时间,防止雪崩)
redisTemplate.opsForValue().set(cacheKey, user, 30, TimeUnit.MINUTES);
System.out.println("缓存写入: " + cacheKey);
} else {
// 4. 防止缓存穿透:对空结果也缓存(短时间)
redisTemplate.opsForValue().set(cacheKey, "", 2, TimeUnit.MINUTES);
}
return user;
}
/**
* 更新用户(先更新 DB,再删除缓存 ------ Cache-Aside 模式)
*/
public void updateUser(User user) {
// 1. 更新数据库
userRepository.update(user);
// 2. 删除缓存(比更新缓存更安全,避免脏数据)
String cacheKey = USER_CACHE_PREFIX + user.getId();
redisTemplate.delete(cacheKey);
System.out.println("缓存已删除: " + cacheKey);
}
/**
* 删除用户
*/
public void deleteUser(Long id) {
userRepository.deleteById(id);
redisTemplate.delete(USER_CACHE_PREFIX + id);
}
}