锁机制 -- 概述篇

news2024/11/22 22:36:50

锁机制

1、概述

​  加锁是为了解决并发场景下,多个线程对同一资源同时进行操作,而导致同一线程多次操作出现结果不唯一的情况(一次操作包含多条指令)。结果不唯一发生的原因在于指令的错乱,前提条件是多线程环境及多个线程的执行顺序是无序的。对于资源A来说,有一次操作[A++] (内包含了多条指令:指令1,读取A的值;指令2,将A的值加1;指令3,将结果值重新赋予A),某一时刻,线程1执行了指令1,2,将要执行指令3时,线程2开始执行,并执行完了指令1,这样最终结果A的值只加了1;另一时刻,线程1执行完了指令1,2,3,线程2再执行指令1,2,3,这样最终结果A的值就加了2。同一操作,不同时刻,最终的结果却不唯一的现象发生了。

​  此时要引出原子性这一概念了,指某一操作已达到最小级别不可继续划分,对于上述操作[A++]来说就,该操作就不是原子性操作,而对于操作[读A的值](内只包含了一条指令:读取A的值)就是原子性操作。

​  而加锁的本质就是通过保证了线程执行操作的有序性,进而保证了操作的原子性。加锁通过对一次操作做出限制,多个线程同时执行该操作时,如若已有线程进行操作,则其他线程必须等待该线程完成后才可进行操作,且一次只允许一个线程进行操作。如此,对于线程来说,该操作是原子性的。

​ 1. 对于 Java 来说,锁机制总体可分为两种,synchronized 关键字和 Lock 接口。

  • synchronized 关键字,可用于修饰普通方法、代码块、静态方法。

  • Lock 接口,定义了lock、lockInterruptibly、tryLock、unlock、newCondition等方法。

​ 2. 对于 MySQL 来说,锁机制总体可分为两种,读锁和写锁,也可称为共享锁和排他锁。

  • 读锁,lock in share mode,允许多个事务对同一行数据加读锁。
  • 写锁,for update,只允许一个事务对同一行数据加写锁,且加写锁之后也不能再加读锁。

​ 3. 对于 Redis 来说,虽然没有直接提供加锁的方式,但提供了 setnx 这一命令可供我们实现加锁。

  • setnx,设置一个 key-value 键值对,如果 key 不存在,则 value 设置成功,并返回 1;如果 key 已存在,则 value 设置失败,并返回 0。

2、Java 中的锁

案例:读写操作下的并发问题

public class LockTest {

    private Integer count = 0;

    public static void main(String[] args) throws InterruptedException {
        LockTest lockTest = new LockTest();
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.execute(lockTest::operate);
        }
        executorService.shutdown();
        TimeUnit.HOURS.sleep(1);
    }

    private void operate() {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 读操作
        if (count == 0) {
            // 写操作
            count = 1;
            System.out.println(Thread.currentThread().getName() + " 加锁成功!");
        }
    }

}

在这里插入图片描述

2.1、使用 synchronized

    private void operate() {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 加锁
        synchronized (this) {
            // 读操作
            if (count == 0) {
                // 写操作
                count = 1;
                System.out.println(Thread.currentThread().getName() + " 加锁成功!");
            }
        }
    }

在这里插入图片描述

2.2、使用 ReentrantLock 类

    private final ReentrantLock lock = new ReentrantLock();

    private void operate() {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 加锁
        lock.lock();
        try {
            // 读操作
            if (count == 0) {
                // 写操作
                count = 1;
                System.out.println(Thread.currentThread().getName() + " 加锁成功!");
            }
        } finally {
            lock.unlock();
        }
    }

在这里插入图片描述

2.3、使用 ReentrantReadWriteLock 类

    private final ReentrantReadWriteLock.WriteLock lock = new ReentrantReadWriteLock().writeLock();

    private void operate() {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 加锁
        lock.lock();
        try {
            // 读操作
            if (count == 0) {
                // 写操作
                count = 1;
                System.out.println(Thread.currentThread().getName() + " 加锁成功!");
            }
        } finally {
            lock.unlock();
        }
    }

在这里插入图片描述

2.4、使用 StampedLock 类

    private final StampedLock stampedLock = new StampedLock();

    private void operate() {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 加锁
        long stamp = stampedLock.writeLock();
        try {
            // 读操作
            if (count == 0) {
                // 写操作
                count = 1;
                System.out.println(Thread.currentThread().getName() + " 加锁成功!");
            }
        } finally {
            stampedLock.unlockWrite(stamp);
        }
    }

在这里插入图片描述

2.5、使用 Semaphore 类

    private final Semaphore semaphore = new Semaphore(1);

    private void operate() {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        try {
            // 加锁
            semaphore.acquire();
            // 读操作
            if (count == 0) {
                // 写操作
                count = 1;
                System.out.println(Thread.currentThread().getName() + " 加锁成功!");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release();
        }
    }

在这里插入图片描述


3、MySQL 中的锁
# 查看事务自动提交是否开启
SHOW VARIABLES LIKE 'autocommit';
# 开启事务自动提交
SET autocommit = 1;
# 关闭事务自动提交
SET autocommit = 0;
# 查看未提交的事务
SELECT * FROM performance_schema.events_transactions_current WHERE STATE = 'ACTIVE';
# 开启事务
BEGIN;
或者 
START TRANSACTION;
# 提交事务
COMMIT;
# 回滚事务
ROLLBACK;

注:MySQL的行锁是在事务结束时自动释放的。

3.1、读锁,lock in share mode

# 事务A
select scene_id, scene_name from scene 
where scene_id = 13
lock in share mode;

# 事务B
select scene_name, scene_logo from scene 
where scene_id = 13
lock in share mode;

在这里插入图片描述
在这里插入图片描述

3.2、写锁,for update

# 事务A
select scene_id, scene_name from scene
where scene_id = 13
for update ;

# 事务B
select scene_name, scene_logo from scene
where scene_id = 13
for update ;

select scene_name, scene_logo from scene 
where scene_id = 13
lock in share mode;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


4、Redis 中的锁

4.1、setnx

# 第一次操作
setnx lock-flag 1

# 第二次操作
setnx lock-flag 0

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

模版总结小全

BFS 最短步数问题 #include<iostream> #include<queue> #include<cstring> using namespace std;const int N 50; char g[N][N],d[N][N]; int dx[] {-1,0,1,0}; int dy[] {0,1,0,-1}; int n,m;int bfs(int x,int y){queue<pair<int,int> > q…

ardupilot开发 --- 坐标变换 篇

Good Morning, and in case I dont see you, good afternoon, good evening, and good night! 0. 一些概念1. 坐标系的旋转1.1 轴角法1.2 四元素1.3 基于欧拉角的旋转矩阵1.3.1 单轴旋转矩阵1.3.2 多轴旋转矩阵1.3.3 其他 2. 齐次变换矩阵3. visp实践 0. 一些概念 相关概念&am…

“论模型驱动架构设计方法及其应用”,软考高级论文,系统架构设计师论文

论文真题 模型驱动架构设计是一种用于应用系统开发的软件设计方法&#xff0c;以模型构造、模型转换和精化为核心&#xff0c;提供了一套软件设计的指导规范。在模型驱动架构环境下&#xff0c;通过创建出机器可读和高度抽象的模型实现对不同问题域的描述&#xff0c;这些模型…

自定义指令directive

一、在src目录下创建一个directive文件夹 test.ts文件存放创建的自定义指令&#xff0c;index.ts用于接收所有指令进行统一处理 二、编写自定义指令 // test.ts文件 export default {// 写个自定义指令mounted(el: any, binding: any) {console.log(el, binding, "&qu…

CC1利用链分析

分析版本 Commons Collections 3.1 JDK 8u65 环境配置参考JAVA安全初探(三):CC1链全分析 分析过程 我的Github主页Java反序列化学习同步更新&#xff0c;有简单的利用链图 首先看下CC1利用链的RCE利用点&#xff0c;在接口Transformer 接下来查看此接口的实现类&#xf…

将json对象转为xml进行操作属性

将json对象转为xml进行操作属性 文章目录 将json对象转为xml进行操作属性前端发送json数据格式写入数据库格式-content字段存储&#xff08;varchar(2000)&#xff09;Question实体类-接口映射对象QuestionContent 接收参数对象DAO持久层Mapper层Service层Controller控制层接收…

谈一下MySQL的两阶段提交机制

文章目录 为什么需要两阶段提交&#xff1f;两阶段提交流程&#xff1f;两阶段提交缺点&#xff1f; 为什么需要两阶段提交&#xff1f; 为了保证事务的持久性和一致性&#xff0c;MySQL需要确保redo log和binlog的同步持久化。MySQL通过“两阶段提交”的机制来实现在事务提交…

MyBatis第一节

目录 1. 简介2. 配置3. doing3.1 创建一个表3.2 打开IDEA&#xff0c;创建一个maven项目3.3 导入依赖的jar包3.4 创建entity3.5 编写mapper映射文件(编写SQL)3.6 编写主配置文件3.7 编写接口3.8 测试 参考链接 1. 简介 它是一款半自动的ORM持久层框架&#xff0c;具有较高的SQ…

【Kubernetes】搭建工具Kubeadm环境配置

架构&#xff1a;服务器采用Master-nodes&#xff08;3台&#xff09; Worker-nodes(2台) 一&#xff0c;服务准备工作 &#xff08;1&#xff09;在所有&#xff08;5台&#xff09;机器配置 主机名绑定&#xff0c;如下&#xff1a; cat /etc/hosts192.168.0.100 k8s-m…

【智能算法】决策树算法

目录 一、基本概念 二、工作原理 三、决策树算法优点和缺点 3.1 决策树算法优点 3.2 决策树算法缺点 四、常见的决策树算法及matlab代码实现 4.1 ID3 4.1.1 定义 4.1.2 matlab代码实现 4.2 C4.5 4.2.1 定义 4.2.2 matlab代码实现 4.3 CART 4.3.1 定义 4.3.2 mat…

leetcode-20-回溯-切割、子集

一、[131]分割回文串 给定一个字符串 s&#xff0c;将 s 分割成一些子串&#xff0c;使每个子串都是回文串。 返回 s 所有可能的分割方案。 示例: 输入: "aab" 输出: [ ["aa","b"], ["a","a","b"] ] 分析&…

springboot是否可以代替spring

Spring Boot不能直接代替Spring&#xff0c;但它是Spring框架的一个扩展和增强&#xff0c;提供了更加便捷和高效的开发体验。以下是关于Spring Boot和Spring关系的详细解释&#xff1a; Spring框架&#xff1a; Spring是一个广泛应用的开源Java框架&#xff0c;提供了一系列模…

Nosql期末复习

mongodb基本常用命令&#xff08;只要掌握所有实验内容就没问题&#xff09; 上机必考&#xff0c;笔试试卷可能考&#xff1a; 1.1 数据库的操作 1.1.1 选择和创建数据库 &#xff08;1&#xff09;use dbname 如果数据库不存在则自动创建&#xff0c;例如&#xff0c;以下…

ElementUI的基本搭建

目录 1&#xff0c;首先在控制终端中输入下面代码&#xff1a;npm i element-ui -S 安装element UI 2&#xff0c;构架登录页面&#xff0c;login.vue​编辑 3&#xff0c;在官网获取对应所需的代码直接复制粘贴到对应位置 4&#xff0c;在继续完善&#xff0c;从官网添加…

【工具分享】Nuclei

文章目录 NucleiLinux安装方式Kali安装Windows安装 Nuclei Nuclei 是一款注重于可配置性、可扩展性和易用性的基于模板的快速漏洞验证工具。它使用 Go 语言开发&#xff0c;具有强大的可配置性、可扩展性&#xff0c;并且易于使用。Nuclei 的核心是利用模板&#xff08;表示为简…

oracle 11g rac安装grid 执行root脚本add vip -n 。。。on node= ... failedFailed 错误处理

问题&#xff1a; CRS-4402: The CSS daemon was started in exclusive mode but found an active CSS daemon on node racdg1-1, number 1, and is terminating An active cluster was found during exclusive startup, restarting to join the cluster PRCN-2050 : The requ…

[OtterCTF 2018]Name Game

Name Game 题目描述&#xff1a;我们知道这个帐号登录到了一个名为Lunar-3的频道。账户名是什么&#xff1f;猜想&#xff1a;既然登陆了游戏&#xff0c;我们尝试直接搜索镜像中的字符串 Lunar-3 。 直接搜索 Lunar-3 先把字符串 重定向到 txt文件里面去然后里面查找 Lunar-3…

阐述Python:except的用法和作用?

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

C++特殊类设计单例模式...

文章目录 请设计一个类&#xff0c;不能被拷贝请设计一个类&#xff0c;只能在堆上创建对象请设计一个类&#xff0c;只能在栈上创建对象请设计一个类&#xff0c;不能被继承请设计一个类&#xff0c;只能创建一个对象(单例模式)单例模式&#xff1a;饿汉模式&#xff1a;懒汉模…

在vs上远程连接Linux写服务器项目并启动后,可以看到服务启动了,但是通过浏览器访问该服务提示找不到页面

应该是被防火墙挡住了&#xff0c;查看这个如何检查linux服务器被防火墙挡住 • Worktile社区 和这个关于Linux下Nginx服务启动&#xff0c;通过浏览器无法访问的问题_linux无法访问nginx-CSDN博客 的提示之后&#xff0c;知道防火墙开了&#xff0c;想着可能是我写的服务器的…