排他制御(楽観ロック・悲観ロック)の基礎 

SELECT语句中的for update的用法(锁的运用)

排他制御(互斥锁)
別名:ロック
【英】lock
排他制御とは、一般に、特定のファイルやデータに対するアクセスや更新を制御することである。特にファイルやデータベースへの書き込み処理を行なう際に、データの整合性を保つためにアクセスやデータの読み書きを一時的に制限すること。

データの参照と書き込みとを常に解放しておくとすれば、二人の管理者が交互にデータを参照し、情報が共有されない状態のままで、各々の作業を反映したデータへと更新する場合が生じうる。このとき先に参照した者と、後に参照した者が、交錯した順序でデータ更新を行えば、実情とデータとは混乱して一致しなくなる恐れもある。

このような事態が生じるのを防ぐために、データを更新する際にはデータに排他制御をかけて、他の者がデータを読み書きできないようにする。そうすれば、他の者は更新完了後のデータのみ参照できるようになるので、実情との整合性が乱されることはない。その際、データの参照と更新との両方を禁ずるか、あるいは参照は許可するかが設定できる。ひとつのプログラムがデータにアクセスして処理を行う間、他の接続口には許可・不許可を示す標識が立てられる。

なお、複数のファイルにまたがって排他制御(ロック)が行われる場合には、ユーザーの一方があるファイルをロックしながら別のあるファイルのロック解除を待ち、もう一方ではその別のファイルをロックしながら先方のロックしたファイルのロック解除を待つ、そのため互いに固めつつ固められ、いつまでたっても処理が進まない、といった事態が生じうる。この現象はデッドロックと呼ばれており、プログラムの設計に問題がある場合に発生する。

并发控制

当程序中可能出现并发的情况时,就需要保证在并发情况下数据的准确性,以此确保当前用户和其他用户一起操作时,所得到的结果和他单独操作时的结果是一样的。这就叫做并发控制。并发控制的目的是保证一个用户的工作不会对另一个用户的工作产生不合理的影响。

没有做好并发控制,就可能导致脏读、幻读和不可重复读等问题。

常说的并发控制,一般都和数据库管理系统(DBMS)有关。在 DBMS 中并发控制的任务,是确保多个事务同时增删改查同一数据时,不破坏事务的隔离性、一致性和数据库的统一性。

实现并发控制的主要手段分为乐观并发控制和悲观并发控制两种。
无论是悲观锁还是乐观锁,都是人们定义出来的概念,可以认为是一种思想。其实不仅仅是关系型数据库系统中有乐观锁和悲观锁的概念,像 hibernate、tair、memcache 等都有类似的概念。所以,不应该拿乐观锁、悲观锁和其他的数据库锁等进行对比。乐观锁比较适用于读多写少的情况(多读场景),悲观锁比较适用于写多读少的情况(多写场景)。

乐观锁

在拿到数据后,不会立即进行数据锁定,只有等到数据需要更新时,才会判断数据是否和那到时一致。
乐观锁的实现机制
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

1、版本号机制,数据有相应的版本来进行确保一致性。
例子:比如A和B同时从数据库获得了数据Money100,这个数据100对应了一个数据版本1,此时A要扣50(100-50),B要扣60(100-60)。B最先提交给了数据库,在提交的时候确认了当前数据库的版本号依旧为1,于是就写入了数据(100-60)剩下40,同时更新了数据版本2。此时A操作完了,也准备写入数据库,本来(100-50)是可以操作成功的,但是A在写入时发现,数据版本变成了2,于是从新从数据库拿到了剩余的数据40,然后再扣款就失败了。

2、CAS算法(compare and swap),无锁编程,在线程没有阻塞的情况下实现变量的同步。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。其基本原理等同于版本号机制,旧的预期值等于数据版本,如果在进行原子操作时,发现旧的预期值和当前的内存值V不相等,则取消操作,否则把B值赋值给内存V。
缺点
乐观锁的问题
乐观锁会带来无限循环问题,也就是俗称的ABA问题的,因为在确认版本数据的同时,很有可能数据再次发生了修改,而程序已经默认为没有修改,这样就会出现漏洞。其次,在最坏情况下,循环检测的乐观锁的开销可以无限大,影响CPU的开销。

  1. ABA 问题
    如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个问题被称为CAS操作的 "ABA"问题。

JDK 1.5 以后的 AtomicStampedReference 类就提供了此种能力,其中的 compareAndSet 方法就是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。

悲观锁

为了防止自己拿到数据后别人会来修改,你就把数据加上锁,直到自己处理完,才会把数据释放给别人。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。
2️⃣悲观锁主要分为共享锁和排他锁:

共享锁【shared locks】又称为读锁,简称 S 锁。顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。
排他锁【exclusive locks】又称为写锁,简称 X 锁。顾名思义,排他锁就是不能与其他锁并存,如果一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁。获取排他锁的事务可以对数据行读取和修改。

场景

从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

比较

两种锁的应用场景,取决于业务类型,如果是多线程对数据的抢夺比较严重的,那就只能用悲观锁,如果大家对数据的抢夺比较少,偶发的,那么从提高效率的角度来说,可以使用乐观锁。通常涉及到写入数据比较频繁的,用悲观锁比较多,如果是读取比较多的,可以用乐观锁。

死锁

一、定义

线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。当线程进入对象的synchronized代码块时,便占有了资源,直到它退出该代码块或者调用wait方法,才释放资源,在此期间,其他线程将不能进入该代码块。当线程互相持有对方所需要的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁。

当然死锁的产生是必须要满足一些特定条件的:
1.互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放
2.请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
3.不剥夺条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用
4.循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。

乐观锁,悲观锁,死锁相关推荐

  1. Java锁详解:“独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁+线程锁”

    在Java并发场景中,会涉及到各种各样的锁如公平锁,乐观锁,悲观锁等等,这篇文章介绍各种锁的分类: 公平锁/非公平锁 可重入锁 独享锁/共享锁 乐观锁/悲观锁 分段锁 自旋锁 线程锁 乐观锁 VS 悲 ...

  2. Mysql之乐观锁悲观锁:乐观锁检查数据状态 悲观锁更新时锁定数据

    1.问题来源 就是一数据表的数据  在两个人同时修改的时候  会出现混乱 例子:如一个字段记录status=1 表示可以下单  货品只有1个的时候    a下单的同时b也下单 : a有修改status ...

  3. mysql默认乐观锁悲观锁_MySQL中悲观锁和乐观锁到底是什么?-阿里云开发者社区...

    索引和锁是数据库中的两个核心知识点,隔离级别的实现都是通过锁来完成的 按照锁颗粒对锁进行划分 ? 锁用来对数据进行锁定,我们可以从锁定对象的粒度大小来对锁进行划分,分别为行锁.页锁和表锁. 行锁就是按 ...

  4. MySQL - 行锁 表锁 乐观锁 悲观锁 读锁 写锁

    MySQL - 行锁 表锁 乐观锁 悲观锁 读锁 写锁 锁是在执行多线程时用于强行限制资源访问的同步机制,即用于在并发控制中保证对互斥要求的满足.在DBMS中,可以按照锁的粒度把数据库锁分为行级锁(I ...

  5. 可重入锁/不可重入锁,公平锁/非公平锁,乐观锁/悲观锁,独享锁/共享锁,偏向锁/轻量级锁/重量级锁,分段锁,自旋锁

    在并发编程中,会涉及到各种各样的锁,这篇文章主要介绍各种锁的分类以及作用. 介绍的内容如下: 可重入锁/不可重入锁 公平锁/非公平锁 乐观锁/悲观锁 独享锁/共享锁 偏向锁/轻量级锁/重量级锁 分段锁 ...

  6. Django - ORM - 事务, 乐观锁, 悲观锁

    事务 概念 Transaction 事务:一个最小的不可再分的工作单元:通常一个事务对应一个完整的业务(例如银行账户转账业务,该业务就是一个最小的工作单元) 一个完整的业务需要批量的DML(inser ...

  7. 最全Java锁详解:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁

    在Java并发场景中,会涉及到各种各样的锁,比如:高并发编程系列:4种常用Java线程锁的特点,性能比较.使用场景,这些锁有对应的种类:公平锁,乐观锁,悲观锁等等,这篇文章来详细介绍各种锁的分类: 公 ...

  8. 【Redis】事物和锁机制乐观锁悲观锁

    目录 1. Redis 的事务定义 2. Multi.Exec.discard 3. 事务的错误处理 4. 事务冲突的问题 悲观锁 乐观锁 1. Redis 的事务定义 Redis 事务是一个单独的隔 ...

  9. Java 面试 :乐观锁 悲观锁

    乐观锁悲观锁,是为了解决多线程并发操作共享变量可能导致的脏读.幻读和不可重复读等问题 悲观锁 悲观锁,是因为这是一种对数据的修改持有悲观态度的并发控制方式.总是假设最坏的情况,每次读取数据的时候都默认 ...

  10. 悲观锁的实现方式java_并发编程--锁--悲观锁和乐观锁

    悲观锁和乐观锁并不是某个具体的"锁"而是一种并发编程的基本概念,是根据看待并发同步的角度.乐观锁和悲观锁最早出现在数据库的设计当中,后来逐渐被 Java 的并发包所引入. 悲观锁 ...

最新文章

  1. Stanford UFLDL教程 实现主成分分析和白化
  2. idea test包_6.Flinkx如何在idea中运行?
  3. 「JupyterNotebook」Jupyter快捷键个人笔记
  4. mysql操作总结_mysql常用操作总结大全
  5. 水仙花python代码_「每日一练」巧用python打印出所有的水仙花数
  6. JavaScript学习笔记——BOM_window对象
  7. 你和文字,陪我行走岁月
  8. Android零基础入门第14节:使用高速Genymotion,跨入火箭时代
  9. php时间戳转UTC时间
  10. FMX控件演示(FireMonkey ControlsDemo)
  11. 单片机十字路口c语言程序,单片机十字路口交通灯控制 C 程序
  12. java 坐标反算_Java实现坐标反算方位角
  13. 计算机现在追寻谁的原理,一路追寻-CS考研经验总结_计算机与软件_考研论坛(kaoyan.com)...
  14. 如何用C语言将华氏温度转化为摄氏温度
  15. Cocos2d-x 3.x游戏开发之旅
  16. 驳《驳〈论OIer谈恋爱的必要性〉》
  17. YOLOv5实现吸烟行为检测
  18. 高客单价项目,适合新手操作的网络项目
  19. ASIC芯片设计生产流程
  20. Meld Diff for windows 安装配置

热门文章

  1. 【方法】树莓派GPIO控制使用教程
  2. vscode强制代码缩进为4个空格
  3. 用python画百变风味月饼
  4. 2022江苏连云港市东海县招聘社区职业化工作者考试试题及答案
  5. “双循环”经济格局下的新一代金融风控
  6. [干货]Android开发人员不得不收集的代码(不断更新)
  7. java实现:使用递归编写一个程序,逆序输出一个非负整数。例如输入1234,输出4321(不含前导0)。
  8. OSChina 周六乱弹 ——2017“荷赛”奖
  9. 前端实例3——透视框(css样式)
  10. java 结束 线程结束_Java结束线程的三种方法(爱奇艺面试)