Java实现分布式锁案例

wen java案例 1

本文目录导读:

Java实现分布式锁案例

  1. 基于Redis的分布式锁
  2. 基于ZooKeeper的分布式锁
  3. 完整的业务使用案例
  4. 使用Redisson的完整示例
  5. 分布式锁要点总结

我来为您提供几种Java实现分布式锁的完整案例,包括基于Redis和ZooKeeper的实现。

基于Redis的分布式锁

使用Jedis实现(最基础版)

import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;
import java.util.UUID;
public class RedisDistributedLock {
    private static final String LOCK_KEY = "distributed_lock";
    private static final long LOCK_EXPIRE_TIME = 30000; // 锁过期时间:30秒
    private Jedis jedis;
    private String lockValue;
    public RedisDistributedLock(Jedis jedis) {
        this.jedis = jedis;
    }
    /**
     * 获取分布式锁
     */
    public boolean tryLock() {
        // 生成唯一标识,用于释放锁时校验
        lockValue = UUID.randomUUID().toString();
        // 使用SET命令的NX和EX参数实现原子性操作
        SetParams params = SetParams.setParams()
                .nx()      // 不存在时才设置
                .ex(30);   // 设置过期时间30秒
        String result = jedis.set(LOCK_KEY, lockValue, params);
        return "OK".equals(result);
    }
    /**
     * 释放分布式锁
     */
    public void unlock() {
        // 使用Lua脚本保证原子性
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                       "return redis.call('del', KEYS[1]) " +
                       "else return 0 end";
        jedis.eval(script, 
                  java.util.Collections.singletonList(LOCK_KEY), 
                  java.util.Collections.singletonList(lockValue));
    }
}

使用Spring Data Redis实现(生产环境推荐)

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
@Component
public class RedisLockUtil {
    @Resource
    private RedisTemplate<String, String> redisTemplate;
    private static final String LOCK_PREFIX = "redis:lock:";
    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";
    /**
     * 加锁
     * @param lockKey 锁的key
     * @param requestId 请求标识(用于防止误删)
     * @param expireTime 过期时间
     * @return 是否成功
     */
    public boolean lock(String lockKey, String requestId, long expireTime) {
        try {
            String key = LOCK_PREFIX + lockKey;
            return Boolean.TRUE.equals(
                redisTemplate.opsForValue().setIfAbsent(key, requestId, expireTime, TimeUnit.SECONDS)
            );
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 解锁
     */
    public boolean unlock(String lockKey, String requestId) {
        try {
            String key = LOCK_PREFIX + lockKey;
            // 使用Lua脚本保证原子性
            DefaultRedisScript<Long> script = new DefaultRedisScript<>(UNLOCK_SCRIPT, Long.class);
            Long result = redisTemplate.execute(script, 
                                              Collections.singletonList(key), 
                                              requestId);
            return result != null && result > 0;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

Redisson分布式锁(功能最全)

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import java.util.concurrent.TimeUnit;
public class RedissonDistributedLock {
    private RedissonClient redissonClient;
    /**
     * 初始化Redisson
     */
    public void init() {
        Config config = new Config();
        config.useSingleServer()
              .setAddress("redis://localhost:6379")
              .setPassword("password")  // 有密码时配置
              .setDatabase(0);
        redissonClient = Redisson.create(config);
    }
    /**
     * 使用Redisson实现分布式锁
     */
    public void operateWithLock() {
        RLock lock = redissonClient.getLock("business-lock");
        try {
            // 尝试加锁,最多等待10秒,锁自动释放时间为30秒
            boolean isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS);
            if (isLocked) {
                try {
                    // 业务处理
                    System.out.println("获取锁成功,执行业务逻辑...");
                    Thread.sleep(5000);
                } finally {
                    // 释放锁(只有持有者才能释放)
                    if (lock.isHeldByCurrentThread()) {
                        lock.unlock();
                    }
                }
            } else {
                System.out.println("获取锁失败");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }
    /**
     * 使用看门狗机制自动续期
     */
    public void operateWithWatchdog() {
        RLock lock = redissonClient.getLock("watchdog-lock");
        try {
            // 默认锁30秒,看门狗每隔10秒自动续期
            lock.lock();
            try {
                // 业务处理(可以长时间运行)
                System.out.println("执行业务逻辑,看门狗会自动续期...");
                Thread.sleep(60000);
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }
}

基于ZooKeeper的分布式锁

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
public class ZookeeperDistributedLock {
    private CuratorFramework client;
    public ZookeeperDistributedLock() {
        // 初始化ZooKeeper客户端
        client = CuratorFrameworkFactory.builder()
                .connectString("localhost:2181")
                .sessionTimeoutMs(5000)
                .connectionTimeoutMs(3000)
                .retryPolicy(new ExponentialBackoffRetry(1000, 3))
                .build();
        client.start();
    }
    /**
     * 使用Curator实现分布式锁
     */
    public void operateWithLock() {
        // 创建分布式锁
        InterProcessMutex lock = new InterProcessMutex(client, "/locks/business-lock");
        try {
            // 获取锁(最多等待10秒)
            boolean isLocked = lock.acquire(10, TimeUnit.SECONDS);
            if (isLocked) {
                try {
                    // 业务处理
                    System.out.println("获取ZooKeeper锁成功,执行业务逻辑...");
                    Thread.sleep(5000);
                } finally {
                    // 释放锁
                    lock.release();
                    System.out.println("释放锁成功");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

完整的业务使用案例

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
@Service
public class InventoryService {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private RedisLockUtil redisLockUtil;
    private static final String INVENTORY_KEY = "inventory:product:1001";
    /**
     * 扣减库存(使用分布式锁)
     */
    public boolean deductStock(String orderId) {
        // 生成请求ID
        String requestId = orderId + ":" + System.currentTimeMillis();
        String lockKey = "product:1001:stock";
        // 尝试获取锁
        boolean isLocked = redisLockUtil.lock(lockKey, requestId, 30);
        if (!isLocked) {
            System.out.println("获取分布式锁失败,订单:" + orderId);
            return false;
        }
        try {
            // 检查库存
            Object stockObj = redisTemplate.opsForValue().get(INVENTORY_KEY);
            int stock = stockObj == null ? 0 : Integer.parseInt(stockObj.toString());
            if (stock <= 0) {
                System.out.println("库存不足,订单:" + orderId);
                return false;
            }
            // 扣减库存
            redisTemplate.opsForValue().set(INVENTORY_KEY, String.valueOf(stock - 1));
            System.out.println("扣减库存成功,剩余库存:" + (stock - 1) + ",订单:" + orderId);
            // 记录订单
            saveOrder(orderId, stock - 1);
            return true;
        } finally {
            // 释放锁
            redisLockUtil.unlock(lockKey, requestId);
        }
    }
    private void saveOrder(String orderId, int remainingStock) {
        // 保存订单到数据库
        System.out.println("保存订单:" + orderId + ",剩余库存:" + remainingStock);
    }
}

使用Redisson的完整示例

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
public class RedissonLockDemo {
    private RedissonClient redissonClient;
    public RedissonLockDemo() {
        Config config = new Config();
        config.useSingleServer()
              .setAddress("redis://localhost:6379")
              .setConnectionPoolSize(10)
              .setConnectionMinimumIdleSize(5);
        redissonClient = Redisson.create(config);
    }
    /**
     * 分布式锁工具方法
     */
    public <T> T executeWithLock(String lockName, 
                                 long waitTime, 
                                 long leaseTime, 
                                 LockCallback<T> callback) {
        RLock lock = redissonClient.getLock(lockName);
        try {
            // 尝试获取锁
            boolean isLocked = lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
            if (!isLocked) {
                throw new RuntimeException("获取锁超时,锁名称:" + lockName);
            }
            try {
                // 执行业务逻辑
                return callback.execute();
            } finally {
                // 释放锁
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("线程被中断", e);
        } finally {
            // 可选:关闭连接
        }
    }
    @FunctionalInterface
    public interface LockCallback<T> {
        T execute();
    }
    // 使用示例
    public static void main(String[] args) {
        RedissonLockDemo demo = new RedissonLockDemo();
        // 执行带锁的业务逻辑
        String result = demo.executeWithLock("product-lock", 10, 30, () -> {
            System.out.println("执行业务逻辑...");
            return "业务执行成功";
        });
        System.out.println("结果:" + result);
    }
}

分布式锁要点总结

核心要点

  • 原子性:加锁和解锁操作必须是原子性的
  • 唯一定标识:每个锁需要唯一标识,防止误删
  • 过期时间:防止死锁
  • 锁续期:处理长任务(Redisson看门狗)

选择建议

方案 优点 缺点 适用场景
Redis SETNX 简单、性能高 可能出现主从切换问题 中小型系统
Redisson 功能完善、自动续期 依赖Redisson客户端 生产环境推荐
ZooKeeper 强一致、节点监控 性能相对较低 对一致性要求高的场景

注意事项

  • 锁的粒度要合适,避免过大或过小
  • 业务执行时间要小于锁的过期时间
  • 考虑高可用部署(Redis集群、ZooKeeper集群)
  • 做好异常处理和降级方案

有任何具体需求或疑问,欢迎继续探讨!

抱歉,评论功能暂时关闭!