乐观锁

公平锁与非公平锁

亡梦爱人 提交于 2020-02-27 10:20:35
前言 Java提供了种类丰富的锁,每种锁因其特性的不同,在适当的场景下能够展现出非常高的效率。本文旨在对锁相关源码(本文中的源码来自JDK 8)、使用场景进行举例,为读者介绍主流锁的知识点,以及不同的锁的适用场景。 Java中往往是按照是否含有某一特性来定义锁,我们通过特性将锁进行分组归类,再使用对比的方式进行介绍,帮助大家更快捷的理解相关知识。下面给出本文内容的总体分类目录: 1. 乐观锁 VS 悲观锁 乐观锁与悲观锁是一种广义上的概念,体现了看待线程同步的不同角度。在Java和数据库中都有此概念对应的实际应用。 先说概念。对于同一个数据的并发操作,悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。Java中,synchronized关键字和Lock的实现类都是悲观锁。 而乐观锁认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新,则根据不同的实现方式执行不同的操作(例如报错或者自动重试)。 乐观锁在Java中是通过使用无锁编程来实现,最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。 根据从上面的概念描述我们可以发现:

悲观锁与乐观锁

一世执手 提交于 2020-02-20 09:14:10
悲观锁 我悲观地认为,我只能通过加锁来解决高并发带来的数据错误问题。 思想: 为查询到的数据进行加锁,当A读这个数据的时候,B就无法读取数据,这解决了问题,但是要知道,加锁的时候消耗很多资源。 乐观锁 我乐观地认为,我可以不通过加锁的方式,就可以解决高并发带来的数据错误问题。 乐观锁的思想主要就是CAS,即Compare And Swap。 当要对查询到的数据进行操作之前,保存一份旧值,当需要修改数据的时候,进行比对,如果说跟原来一样,那就说明没人动过这份数据,可以继续进行,否则的话,说明数据有可能出现并发修改的数据错误,那就不再进行数据修改的操作。 以此来保证数据的正确性。 乐观锁的弊端即解决方案 CAS思想会引发ABA问题,即数据A经过修改变成了B,后面又变回了A,这对乐观锁处理并发问题是致命的,因为假如出现了ABA问题,CAS就会发现旧值和当前值相同,即会进行数据的修改,殊不知,数据已然被修改过。 一种比较典型的解决方案就是增加版本号字段,每次进行修改数据后,都让版本号+1,即使出现了ABA问题,版本号依旧是变的,从而能避免错误。 来源: CSDN 作者: 阳光大男孩!!! 链接: https://blog.csdn.net/weixin_43889841/article/details/104397333

接口幂等性的解决方案

余生长醉 提交于 2020-02-17 09:00:55
接口幂等性的解决方案 在编程中,幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数指的是那些使用相同参数重复执行也能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。比如说getIdCard()函数和setTrue()函数就是幂等函数。 幂等在我的理解里就是,一个操作不论被执行多少次,产生的效果和返回的结果都是一样的。 一个幂等的操作典型如:把编号为5的记录的A字段设置为0这种操作不管执行多少次都是幂等的。 一个非幂等的操作典型如:把编号为5的记录的A字段增加1这种操作显然就不是幂等的。 幂等的方案 1.查询操作:Select是天然的幂等操作。 查询一次和查询多次,在数据不变的情况下,查询的结果都是一样的。 2.删除操作:删除操作也是幂等的,删除一次和删除多次都是把数据删除。 因为删除操作通常是定向的,比如通过id去删除数据,如果该id在数据库中存在对应记录,则删除该记录;如果该id在数据库中不存在对应记录,也是执行的删除记录操作,只是没有实质性地删除到记录而已,却也不会有其他的副作用。 但是如果删除操作具有返回值的话,可能返回的结果会不一样,比如删除一条记录之后返回这条记录中的某个值,如果删除的数据不存在(已经在第一次的删除请求中被删除了),返回的就是空值了。 3.唯一索引

乐观锁和悲观锁的区别

余生颓废 提交于 2020-02-15 06:36:54
乐观锁 在关系数据库管理系统里,乐观并发控制(又名”乐观锁”,Optimistic Concurrency Control,缩写”OCC”)是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。乐观事务控制最早是由孔祥重(H.T.Kung)教授提出。 乐观并发控制的阶段 乐观并发控制的事务包括以下阶段: 1. 读取:事务将数据读入缓存,这时系统会给事务分派一个时间戳。 2. 校验:事务执行完毕后,进行提交。这时同步校验所有事务,如果事务所读取的数据在读取之后又被其他事务修改,则产生冲突,事务被中断回滚。 3. 写入:通过校验阶段后,将更新的数据写入数据库。 乐观并发控制多数用于数据争用不大、冲突较少的环境中,这种环境中,偶尔回滚事务的成本会低于读取数据时锁定数据的成本,因此可以获得比其他并发控制方法更高的吞吐量。 相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本。 数据版本,为数据增加的一个版本标识。当读取数据时,将版本标识的值一同读出,数据每更新一次,同时对版本标识进行更新。当我们提交更新的时候

转载---乐观锁和悲观锁

冷暖自知 提交于 2020-02-14 21:45:35
本文转载自链接: https://blog.csdn.net/qq_34337272/article/details/81072874 何谓悲观锁与乐观锁 乐观锁对应于生活中乐观的人总是想着事情往好的方向发展,悲观锁对应于生活中悲观的人总是想着事情往坏的方向发展。这两种人各有优缺点,不能不以场景而定说一种人好于另外一种人。 悲观锁 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。 乐观锁 总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。 两种锁的使用场景

乐观锁,悲观锁,表锁,行锁,共享锁,排他锁

淺唱寂寞╮ 提交于 2020-02-09 08:22:55
乐观锁 乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了。  实现方式1:在表中的数据进行操作时(更新),先给数据表加一个版本(version)字段,每操作一次,将那条记录的版本号加1。也就是先查询出那条记录,获取出version字段,如果要对那条记录进行操作(更新),则先判断此刻version的值是否与刚刚查询出来时的version的值相等,如果相等,则说明这段期间,没有其他程序对其进行操作,则可以执行更新,将version字段的值加1;如果更新时发现此刻的version值与刚刚获取出来的version的值不相等,则说明这段期间已经有其他程序对其进行操作了,则不进行更新操作。 举例:假设莫某个商品是有库存才能下单,有库存的状态是1,没有库存状态变为2,用状态status表示。 下单操作包括3步骤: 1.查询出商品信息 select (status,status,version) from t_goods where id=#{id} 2.根据商品信息生成订单 3.修改商品status为2 update t_goods set status=2,version=version+1 where id=#{id} and version=#{version};

mybatis-plus 乐观锁

[亡魂溺海] 提交于 2020-02-06 21:53:47
参见: https://mp.baomidou.com/guide/optimistic-locker-plugin.html#%E4%B8%BB%E8%A6%81%E9%80%82%E7%94%A8%E5%9C%BA%E6%99%AF 原理: 场景1:A获取到的version=0,而 此时B修改并提交了即version=1,然后A在update提交,则不成功。 场景2:A获取到的version=0,在B修改并提交之前,A执行update提交成功,则当B在执行update提交时不成功。 即:不管别人先提交了,还是自己先提交了,不能让后提交的人修改成功 不让其修改成功的方法是:UPDATE t_sch_locale SET modified_time=?, creator=?, locale_detail=?, remark=?, state=?, locale_name=?, version=? WHERE id=? AND version=? AND is_delete=0 这个 ? 就是传过来的值,如果不一样了,就返回结果为0 举个例子,以下是接收update提交的方法体: LocalePO po = new LocalePO();//localeMapper.selectById(dto.getId()); BeanCopyUtils.copyBean(dto.dto2po(

Redis的事务功能详解

£可爱£侵袭症+ 提交于 2020-02-05 14:04:41
Redis的事务功能详解 MULTI、EXEC、DISCARD和WATCH命令是Redis事务功能的基础 。Redis事务允许在一次单独的步骤中执行一组命令,并且可以保证如下两个重要事项: > Redis会将一个事务中的所有命令序列化 ,然后按顺序执行。Redis不可能在一个Redis事务的执行过程中插入执行另一个客户端发出的请求。这样便能保证Redis将这些命令作为一个单独的隔离操作执行。 > 在一个Redis事务中,Redis要么执行其中的所有命令,要么什么都不执行 。 因此,Redis事务能够保证原子性 。EXEC命令会触发执行事务中的所有命令。因此,当某个客户端正在执行一次事务时,如果它在调用MULTI命令之前就从Redis服务端断开连接,那么就不会执行事务中的任何操作;相反,如果它在调用EXEC命令之后才从Redis服务端断开连接,那么就会执行事务中的所有操作。当Redis使用只增文件(AOF:Append-only File)时,Redis能够确保使用一个单独的write(2)系统调用,这样便能将事务写入磁盘。然而,如果Redis服务器宕机,或者系统管理员以某种方式停止Redis服务进程的运行,那么Redis很有可能只执行了事务中的一部分操作。Redis将会在重新启动时检查上述状态,然后退出运行,并且输出报错信息。使用redis-check

乐观锁 和 悲观锁 的用法和区别

余生颓废 提交于 2020-02-01 01:16:07
随着并发量的增加影响到我们数据时, 我们可以用MySQL的锁技术 悲观锁 在执行数据库操作的时候,会加一把锁, 事物提交后释放锁 (期间如果有别的线程进行数据库操作, 会阻塞, 如果一直占用资源不释放,其他线程就一直无法操作数据) 需配合MySQL的事物进行操作 使用方法 在数据库操作语句中加入 for update 如 : select * from goods where id = 1 for update 乐观锁 不会加锁, 但是更新的时候会进行判断, 判断跟原始的数据一不一样,返回受影响行数, 如果不一样,他会返回受影响的行数为0, 如果一样,会返回受影响的具体数量, 我们可以通过返回值,来采取对应的措施,比如回滚还是从新执行一遍 原理: 将 select 和 update 融为 一条语句, 并返回受影响的行数, 如果行数为0,说明别人已经把数据改了, 这个时候我们可以再尝试重新获取一下数据 使用方法(拿Django来说) 将 select 和 update 融为 一条语句 GoodsSKU . objects . filter ( id = sku_id , stock = orgin_stock ) . update ( stock = new_stock , sales = new_sales ) 总结 冲突比较少的时候, 使用乐观锁(没有悲观锁那样耗时的开销)

Java多线程的悲观锁与乐观锁

こ雲淡風輕ζ 提交于 2020-01-31 05:23:29
转载请注明原文地址: http://www.cnblogs.com/ygj0930/p/6561376.html 一:悲观锁 悲观锁,就是不管是否发生多线程冲突,只要存在这种可能,就每次访问都加锁,加锁就会导致锁之间的争夺,有争夺就会有输赢,输者等待。 syncrhoized是一种独占锁,即:占用该锁的线程才可以执行,申请该锁的线程就只能挂起等待,直到占用锁的线程释放锁才唤醒,拿到锁并执行。由于在进程挂起和恢复执行过程中存在着很大的开销,并且当一个线程正在等待锁时,它不能做任何事。所以syncrhoized是一种悲观锁,凡是用syncrhoized加了锁的多线程之间都会因锁的争夺结果导致挂起、唤醒等开销。 二:乐观锁 获得锁后一直持有锁以防本线程再次申请该锁造成无谓的解锁再加锁开销,或者假设没有冲突而去完成同步代码块如果冲突再循环重试,或者采取申请锁失败后不立刻挂起而是稍微等待再次尝试获取 等待策略,以减少线程因为挂起、阻塞、唤醒(发生CPU的调度切换) 而造成的开销。 偏向锁、轻量级锁(CAS轮询)、自旋锁 就是基于上述思路的乐观锁。 在多线程的加锁机制中,JVM会首先尝试乐观锁,失败后才调用悲观锁。 来源: https://www.cnblogs.com/ygj0930/p/6561376.html