分布式锁与同步锁

news2024/9/24 23:30:50

在这里插入图片描述

什么是分布式锁

分布式锁是一种在分布式系统中用于协调多个节点访问共享资源的机制。在分布式系统中,由于存在多个节点并行执行任务,可能会出现竞争条件和数据不一致的问题。分布式锁通过约束同一时刻只有一个节点能够获得锁的方式,确保了对共享资源的独占访问,从而解决了这些问题。

分布式锁的实现通常需要满足以下特性:

  1. 互斥性:同一时刻只有一个节点能够持有锁,并且其他节点无法获取该锁。

  2. 可重入性:允许同一节点多次获取同一个锁,而不会发生死锁。

  3. 容错性:在锁持有者节点故障或网络异常情况下,能够及时释放锁。

  4. 超时处理:支持设置获取锁的最大等待时间,避免由于死锁或长时间阻塞导致系统性能下降。

  5. 高可用性:具备高可用性能,即使部分节点故障也能正常提供服务。

实现分布式锁的方法有多种,常见的包括:

  1. 基于数据库的分布式锁:利用数据库的事务和唯一索引等特性,使用数据库表记录锁的状态。通过获取和释放特定的行锁来实现互斥访问。

  2. 基于缓存的分布式锁:利用分布式缓存(如Redis)的原子性操作和过期时间等特点,通过设置一个全局唯一的键值对表示锁的状态。只有成功获取到锁的节点才能在缓存中设置这个键值对,其他节点则无法设置并获取到锁。

什么是分布式锁

同步锁是一种并发控制机制,用于在多线程环境下保护共享资源的访问。它可以防止多个线程同时访问临界区代码,从而避免并发访问导致的数据不一致或冲突。

同步锁的原理是通过获取锁来获得对临界区代码的独占访问权。在Java中,常用的同步锁机制包括内置锁(也称为监视器锁)和显式锁。

  1. 内置锁(Intrinsic Lock):也称为对象监视器锁或synchronized锁。当一个方法或代码块被synchronized修饰时,该对象上就存在一个内置锁。只有获得了该锁的线程才能执行被修饰的方法或代码块,其他线程必须等待锁的释放。

示例代码:

public class Example {
    private Object lock = new Object();
    
    public void synchronizedMethod() {
        synchronized (lock) {
            // 临界区代码
        }
    }
}
  1. 显式锁(Explicit Lock):使用java.util.concurrent.locks包中的Lock接口及其实现类,如ReentrantLock。相比内置锁,显式锁提供了更灵活的锁定机制,如可重入性、公平性和条件变量等特性,使得多线程代码的控制更加精确。

示例代码:

public class Example {
    private Lock lock = new ReentrantLock();
    
    public void lockedMethod() {
        lock.lock();
        try {
            // 临界区代码
        } finally {
            lock.unlock();
        }
    }
}

同步锁的使用可以有效避免多个线程对共享资源的不安全访问,保证数据一致性和并发执行的正确性。但过度使用同步锁可能导致线程竞争和性能下降,因此在设计多线程应用程序时需要合理地使用同步锁机制。

两种锁的使用场景

分布式锁和同步锁是在不同环境下用于实现并发控制的两种机制。

  1. 分布式锁的使用场景:
    分布式锁通常用于分布式系统中,用于协调多个节点对共享资源的访问。一个常见的场景是在分布式环境下实现对某个唯一资源的排他性访问,例如分布式任务调度、分布式缓存更新等。

示例代码(基于Redis实现分布式锁):

import redis.clients.jedis.Jedis;

public class DistributedLock {
    private Jedis jedis;
    private String lockKey;

    public boolean acquireLock() {
        long result = jedis.setnx(lockKey, "locked");
        return result == 1;
    }

    public void releaseLock() {
        jedis.del(lockKey);
    }
}

在上述代码中,通过调用 setnx() 方法尝试获取锁,如果返回值为1,则表示成功获取到分布式锁。释放锁的操作则是调用 del() 方法删除锁的键。

  1. 同步锁的使用场景:
    同步锁主要用于多线程编程中,控制多个线程对共享资源的并发访问。一个经典的场景是在多线程环境下保护对某个临界区代码的独占访问,避免数据竞争和冲突。

示例代码(基于Java内置锁实现同步锁):

public class SynchronizedCounter {
    private int count;

    public synchronized void increment() {
        count++;
    }

    public synchronized void decrement() {
        count--;
    }
}

在上述代码中,通过 synchronized 关键字修饰方法,使得多线程在执行这些方法时会自动获取对象的内置锁。这样可以确保同一时间只能有一个线程执行 increment()decrement() 方法,避免并发访问

分布式锁实现

分布式锁是在分布式系统中用于实现资源的互斥访问的一种机制。下面介绍两种常见的分布式锁实现方法:

  1. 基于数据库的分布式锁:
    使用数据库作为分布式锁的持久化存储,通过在表中创建唯一索引或使用悲观锁来确保只有一个节点能够成功获取锁。

    // 获取分布式锁
    public boolean acquireLock(String lockName) {
        try (Connection connection = dataSource.getConnection()) {
            String sql = "INSERT INTO distributed_lock (lock_name) VALUES (?)";
            PreparedStatement statement = connection.prepareStatement(sql);
            statement.setString(1, lockName);
            int affectedRows = statement.executeUpdate();
            return affectedRows > 0;
        } catch (SQLException e) {
            // 处理异常
        }
        return false;
    }
    
    // 释放分布式锁
    public void releaseLock(String lockName) {
        try (Connection connection = dataSource.getConnection()) {
            String sql = "DELETE FROM distributed_lock WHERE lock_name = ?";
            PreparedStatement statement = connection.prepareStatement(sql);
            statement.setString(1, lockName);
            statement.executeUpdate();
        } catch (SQLException e) {
            // 处理异常
        }
    }
    

    在上述示例中,使用数据库表 distributed_lock 存储分布式锁的状态。通过执行相应的SQL语句来获取锁和释放锁,确保只有一个节点能够插入对应的锁名称到表中。其他节点尝试插入同样的锁名称会因为唯一索引或悲观锁失败,从而实现了互斥访问。

  2. 基于Redis的分布式锁:
    使用Redis作为分布式锁的存储中心,利用Redis的原子操作特性和过期时间来实现分布式锁的获取和释放。

    public boolean acquireLock(String lockKey, String requestId, int expireTime) {
        try (Jedis jedis = jedisPool.getResource()) {
            String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
            return "OK".equalsIgnoreCase(result);
        } catch (Exception e) {
            // 处理异常
        }
        return false;
    }
    
    public void releaseLock(String lockKey, String requestId) {
        try (Jedis jedis = jedisPool.getResource()) {
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
        } catch (Exception e) {
            // 处理异常
        }
    }
    

    在上述示例中,使用Redis的 set 命令以原子方式将锁信息存储到Redis中,并设置过期时间。通过获取锁时传递一个唯一的请求标识符 requestId,并在释放锁时校验该标识符,确保只有持有锁的节点能够释放它。

这两种分布式锁的实现方法各有优劣,选择合适的方式取决于具体业务场景和系统需求。分布式锁的设计和实现需要考虑到分布式环境下的并发性、可靠性和性能等方面的问题,确保在多个节点之间实现正确的资源互斥访问。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/758196.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【计算机视觉】DINOv2(Facebook自监督视觉学习)的环境部署和使用代码示范(含源代码)

文章目录 一、导读二、环境部署三、使用示例 我的代码示范已经上传了Kaggle平台,具体的笔记地址为: https://www.kaggle.com/code/holmes0610/dinov2一、导读 DINOv2:在没有监督的情况下学习鲁棒的视觉特征 这是第一种训练计算机视觉模型的…

2023爱分析·AIGC厂商全景报告|爱分析报告

关键发现 通用大模型市场当前虽入局者众多,但终局将高度集中,未来将由5-6家厂商主导;应用层厂商及甲方企业应着眼终局,从大算力、巨量数据集、端到端AI工程化能力以及应用生态伙伴等维度综合考虑,慎重选择合作伙伴。 …

【LeetCode每日一题合集】2023.7.10-2023.7.16

文章目录 16. 最接近的三数之和排序 双指针 1911. 最大子序列交替和解法——动态规划 2544. 交替数字和(简单模拟)931. 下降路径最小和(线性DP)979. 在二叉树中分配硬币⭐⭐⭐⭐⭐(dfs)18. 四数之和&#…

[JAVA]程序逻辑控制,输入输出

🍉内容专栏:【JAVA】 🍉本文脉络:程序逻辑控制,if语句,switch循环,while循环,for循环,do while循环输入输出,例子 🍉本文作者:Melon_西…

Python:基于matplotlib与mayavi的3D可视化

文章目录 (1)基于matplotlib的3D可视化(2)基于mayavi的3D可视化(2.1)立方体(2.2)体素体 3D可视化是一种用于呈现三维数据的方法,它可以帮助我们更好地理解和分析复杂的空…

搭建 Java 部署环境,部署 Web 项目到 Linux

为了进行部署,把写好的 java web 程序放到 Linux 上,需要先把对应的依赖的软件 (环境) 搭建好,安装一些必要的软件程序 JDKTomcatMySqL jdk 直接使用包管理器进行安装(基于yum安装) 一、yum 1、认识 yum yum (Yellow dog Updater, Modified…

随机产生50个55以内的不重复的整数,要求查找出一个缺失的整数。

一、设计思路 为随机产生50个55以内的整数且不能重复,应先将已经产生的随机数保存下来,然后在获取到新的随机数时,与之前保存下来的值进行对比,如果已经出现过则抛弃,并再次获取,直到获取到不同的随机值为止…

Redis 从入门到精通【进阶篇】之过期和淘汰策略详解

文章目录 0. 前言Redis过期策略:1. 定期删除(定时器方式):2. 惰性删除(访问时检查方式): Redis淘汰策略:1. noeviction:2. allkeys-lru:3. volatile-lru&…

GOPATH、GOROOT(VSCode编写第一个go程序)

1. GOROOT和GOPATH GOROOT 和 GOPATH 都是 Go 语言中的环境变量,用于指定 Go 工具链和工作区的路径。 GOROOT 指定了 Go 工具链的安装路径,它包含了 Go 语言的标准库、编译器等工具。在使用 Go 编译器、运行时等工具时,它们会默认从 GOROOT…

Linux ➾ 端口占用检查

Linux ➾ 端口占用检查 🔻 前言🔻 一、什么是监听端口🔻 二、使用lsof命令查看端口占用情况🔻 三、使用netstat命令检查监听端口🔻 四、使用ss 检查监听端口🔻 总结—温故知新 🔻 前言 在Linux系…

举例说明基于线性回归的单层神经网络网络(以梯度下降算法来求解权重的过程)...

我们将通过一个简单的例子来说明基于线性回归的单层神经网络,以及如何使用梯度下降算法来求解权重。 假设我们有以下数据集,表示学生的学习时间(小时)与他们的考试分数: 学习时间(X)&#xff1a…

C# 反转无头链。

给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。 数据范围: 0≤n≤1000 要求:空间复杂度 O(1) ,时间复杂度 O(n) 。…

【SpringBoot应用篇】SpringBoot集成atomikos实现多数据源配置和分布式事务管理

【SpringBoot应用篇】SpringBoot集成atomikos实现多数据源配置和分布式事务管理 分布式事务概念XA和JTA概述SpringBoot集成atomikos数据库结构pom通用工具类RBaseControllerBaseExceptionCodeExceptionCodeBaseExceptionBaseUncheckedExceptionBizException application.yml数据…

【C语言】扫雷----详解(扩展版)

🍁 博客主页:江池俊的博客_CSDN博客 🍁 如果觉得博主的文章还不错的话,请点赞👍收藏🌟 三连支持一下博主💞 ✉️每一次努力都是一次进步,每一次尝试都是一次机会。无论遇到什么困难,…

SpringBoot 使用前缀树实现敏感词过滤

文章目录 前缀树介绍节点初始化前缀树添加敏感词删除敏感词敏感词过滤代码实现 前缀树介绍 前缀树(Trie),也称为字典树或前缀字典树,是一种特殊的多叉树数据结构。它用于高效地存储和检索字符串集合。以下是前缀树的常见数据结构…

verilog实现led闪烁

文章目录 verilog实现led闪烁一、介绍二、代码三、仿真代码四、仿真结果五、总结 verilog实现led闪烁 一、介绍 使用verilog实现代码,实现led闪烁,每间隔200ms进行切换led灯 二、代码 module led (input wire clk,input wire rstn,output wire[3:0] …

深入解析向量数据库:定义、原理和应用的全面指南

🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~&#x1f33…

C++智能指针(3/3)

目录 上一节内容 share_ptr用法 share_ptr指针可以用于上一节所说的错误 例子&#xff08;类定义&#xff09; 主函数代码 执行的结果 解释说明 share_ptr 相关构造 空的share指针可以指向其他相同类型的变量来进行托管 可以shared_ptr< T > sp2(new T())也可以s…

RocketMQ高阶使用

RocketMQ高阶使用 1. 流程 2. 探讨功能点 RocketMQ的顺序消息消息投递策略消息保障 3. 顺序消息 3.1 顺序类型 3.1.1 无序消息 无序消息也指普通的消息&#xff0c;Producer 只管发送消息&#xff0c;Consumer 只管接收消息&#xff0c;至于消息和消息之间的顺序并没有保证…

macOS搭建C++开发环境CLion

首先我是一个java开发者&#xff0c;最近对C产生点兴趣。想开发点C程序玩一玩。 下载IDE 本人是java开发者&#xff0c;习惯使用IDEA了。所以也下载jetbrains的C开发工具:clion 下载地址&#xff1a; https://www.jetbrains.com/clion/download/#sectionmac Hello world Fi…