史上最全的java分布式锁的5种实现方式

2023-03-23 12:20:19 浏览数 (1)

  1. 基于Redis实现分布式锁

Redis是一个高性能的内存数据库,支持分布式锁的实现。基于Redis实现分布式锁的步骤如下:

(1)获取Redis连接

(2)使用setnx命令设置键值对,如果返回值为1,则表示获取锁成功,否则获取锁失败

(3)如果获取锁失败,则使用get命令获取锁的值,并判断当前时间是否大于锁的超时时间,如果是,则使用getset命令设置新的锁值,并判断返回的值是否与获取的值相等,如果相等,则表示获取锁成功,否则获取锁失败

(4)使用del命令删除锁

示例代码如下:

代码语言:txt复制
public class RedisLock {
    
    private Jedis jedis;
    private String lockKey;
    private int expireTime = 30000;
    private int timeout = 10000;
    private boolean locked = false;
    
    public RedisLock(Jedis jedis, String lockKey) {
        this.jedis = jedis;
        this.lockKey = lockKey;
    }
    
    public boolean lock() throws InterruptedException {
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < timeout) {
            long expires = System.currentTimeMillis()   expireTime   1;
            String expiresStr = String.valueOf(expires);
            if (jedis.setnx(lockKey, expiresStr) == 1) {
                locked = true;
                return true;
            }
            String currentValueStr = jedis.get(lockKey);
            if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
                String oldValueStr = jedis.getSet(lockKey, expiresStr);
                if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
                    locked = true;
                    return true;
                }
            }
            Thread.sleep(1000);
        }
        return false;
    }
    
    public void unlock() {
        if (locked) {
            jedis.del(lockKey);
            locked = false;
        }
    }
}
  1. 基于ZooKeeper实现分布式锁

ZooKeeper是一个分布式协调服务,支持分布式锁的实现。基于ZooKeeper实现分布式锁的步骤如下:

(1)创建一个ZooKeeper客户端连接

(2)使用create命令创建一个临时节点,如果创建成功,则表示获取锁成功,否则获取锁失败

(3)如果获取锁失败,则使用exists命令监听锁节点的删除事件,并等待锁释放

(4)使用delete命令删除锁节点

示例代码如下:

代码语言:txt复制
public class ZooKeeperLock implements Watcher {
    
    private ZooKeeper zooKeeper;
    private String lockPath;
    private String lockNode;
    private CountDownLatch countDownLatch = new CountDownLatch(1);
    private boolean locked = false;
    
    public ZooKeeperLock(ZooKeeper zooKeeper, String lockPath) {
        this.zooKeeper = zooKeeper;
        this.lockPath = lockPath;
    }
    
    public boolean lock() throws InterruptedException, KeeperException {
        lockNode = zooKeeper.create(lockPath   "/lock-", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        List<String> nodes = zooKeeper.getChildren(lockPath, false);
        Collections.sort(nodes);
        if (lockNode.equals(lockPath   "/"   nodes.get(0))) {
            locked = true;
            return true;
        } else {
            String prevNode = lockPath   "/"   nodes.get(Collections.binarySearch(nodes, lockNode.substring(lockNode.lastIndexOf("/")   1)) - 1);
            zooKeeper.exists(prevNode, this);
            countDownLatch.await();
            return locked;
        }
    }
    
    public void unlock() throws InterruptedException, KeeperException {
        zooKeeper.delete(lockNode, -1);
        locked = false;
    }
    
    @Override
    public void process(WatchedEvent event) {
        if (event.getType() == Event.EventType.NodeDeleted) {
            countDownLatch.countDown();
        }
    }
}
  1. 基于数据库实现分布式锁

数据库可以通过加锁机制来实现分布式锁。基于数据库实现分布式锁的步骤如下:

(1)使用select for update命令查询锁,如果查询结果为空,则表示获取锁成功,否则获取锁失败

(2)如果获取锁失败,则使用select命令查询锁,并设置超时时间,等待锁释放

(3)使用update命令更新锁

示例代码如下:

代码语言:txt复制
public class DatabaseLock {
    
    private Connection connection;
    private String lockTable;
    private String lockName;
    private int expireTime = 30000;
    private int timeout = 10000;
    private boolean locked = false;
    
    public DatabaseLock(Connection connection, String lockTable, String lockName) {
        this.connection = connection;
        this.lockTable = lockTable;
        this.lockName = lockName;
    }
    
    public boolean lock() throws SQLException, InterruptedException {
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < timeout) {
            PreparedStatement selectStatement = connection.prepareStatement("SELECT * FROM "   lockTable   " WHERE name = ? FOR UPDATE");
            selectStatement.setString(1, lockName);
            ResultSet resultSet = selectStatement.executeQuery();
            if (!resultSet.next()) {
                PreparedStatement insertStatement = connection.prepareStatement("INSERT INTO "   lockTable   " (name, expires) VALUES (?, ?)");
                insertStatement.setString(1, lockName);
                insertStatement.setTimestamp(2, new Timestamp(System.currentTimeMillis()   expireTime));
                insertStatement.executeUpdate();
                locked = true;
                return true;
            }
            Timestamp expires = resultSet.getTimestamp("expires");
            if (expires != null && expires.getTime() < System.currentTimeMillis()) {
                PreparedStatement updateStatement = connection.prepareStatement("UPDATE "   lockTable   " SET expires = ? WHERE name = ? AND expires = ?");
                updateStatement.setTimestamp(1, new Timestamp(System.currentTimeMillis()   expireTime));
                updateStatement.setString(2, lockName);
                updateStatement.setTimestamp(3, expires);
                int affectedRows = updateStatement.executeUpdate();
                if (affectedRows > 0) {
                    locked = true;
                    return true;
                }
            }
            Thread.sleep(1000);
        }
        return false;
    }
    
    public void unlock() throws SQLException {
        if (locked) {
            PreparedStatement deleteStatement = connection.prepareStatement("DELETE FROM "   lockTable   " WHERE name = ?");
            deleteStatement.setString(1, lockName);
            deleteStatement.executeUpdate();
            locked = false;
        }
    }
}
  1. 基于文件系统实现分布式锁

文件系统可以通过文件锁来实现分布式锁。基于文件系统实现分布式锁的步骤如下:

(1)使用FileChannel的tryLock方法获取文件锁,如果获取锁成功,则表示获取锁成功,否则获取锁失败

(2)如果获取锁失败,则使用FileChannel的lock方法获取文件锁,并等待锁释放

(3)使用FileChannel的release方法释放文件锁

示例代码如下:

代码语言:txt复制
public class FileLock {
    
    private FileChannel fileChannel;
    private FileLock lock;
    private boolean locked = false;
    
    public FileLock(File file) throws IOException {
        fileChannel = new RandomAccessFile(file, "rw").getChannel();
    }
    
    public boolean lock() throws IOException, InterruptedException {
        lock = fileChannel.tryLock();
        if (lock != null) {
            locked = true;
            return true;
        } else {
            lock = fileChannel.lock();
            lock = fileChannel.tryLock();
            locked = true;
            return true;
        }
    }
    
    public void unlock() throws IOException {
        if (locked) {
            lock.release();
            fileChannel.close();
            locked = false;
        }
    }
}
  1. 基于Spring实现分布式锁

Spring提供了分布式锁的实现,可以使用Spring进行分布式锁的操作。基于Spring实现分布式锁的步骤如下:

(1)使用@Lock注解标注需要加锁的方法

(2)使用@LockKey注解标注锁的键值

(3)使用@EnableLock注解开启分布式锁

示例代码如下:

代码语言:txt复制
@EnableLock
public class SpringLock {
    
    @Lock(key = "lockKey")
    public void doSomething() {
        // do something
    }
    
    @Lock(key = "#id")
    public void doSomething(@LockKey String id) {
        // do something
    }
}

0 人点赞