乐观锁&悲观锁

并发锁

  生产环境中,并发场景是十分常见的,多个线程的并发天然会难以免的访问到共享资源。而一旦出现多个线程共享同一个资源的时候,就会出现数据混乱的问题。最多见的就是先判断后操做的场景,举个例子,小明和小黄吃坏了肚子,想要上厕所,结果他俩同时看到一个空的位置,因而都认为没人本身能够上厕所,结果两我的都挤在了厕所里。那么该如何解决这个问题呢,没错,咱们能够加上一把锁,小明上厕所的时候把门关上,小黄阻塞着等待。经过将并发变为按序单一执行,从而解决数据混乱的问题。java

  下面咱们就介绍一下经常使用的乐观锁和悲观锁,以及在Java中的应用。mysql

乐观锁(CAS)

  • 乐观锁:顾名思义老是乐观的认为这个数据没有在同一时间被其余人操做变动sql

  • 实现:CAS(compare and set)数据库

  • 优势:吞吐量高,适用于数据冲突相对较少的场景多线程

  • 缺点:受限于外部系统,可能会引发脏读,且在数据冲突很大的场景下,性能反而可能更低并发

咱们主要介绍乐观锁思想的实现CAS,即compare and set,先比较再设置。仍是以以前小明小黄为例,即在上厕所前先作一次和以前认为的状况作一次比较(以前认为没人),若是一致则上厕所。核心思路就是,每次不加锁而是假设没有冲突而去完成某项操做,若是由于冲突失败就重试,直到成功为止。下面咱们介绍一下CAS在java中的实现性能

AtomicInteger & AtomicReference<T>spa

  AtomicInteger & AtomicReference<T>这两个类,相比你们都很熟悉。Java中提供了这些原子类,来解决在多线程并发场景下共享数据的混乱问题,而其实现思想即为CAS。而其底层实现的native方法依赖于外部系统,利用JNI来完成CPU指令的操做。咱们来分析一下代码便可知线程

ABA问题3d

  当时光是这样如此还存在着一个问题,假设线程2将值A变为B以后又将B变为A,此时线程2过来发现值仍是A,单其实并非原来的A了,却仍将其值变为了C。咱们经过代码的方式来看一下。

面对这种问题,咱们能够设置一个版本号,在比对值的同时,在增长对版本号的比对。以此来解决ABA的问题,在Java提供了AtomicStampedReference类来实现带版本号的cas操做。

数据库实现乐观锁

  除了利用Java底层依赖CPU指令来完成CAS操做,咱们还能够借用数据库来实现。其核心思想仍然与上面的相同,实现的核心在于如下的sql

  update goods set num= newnum, version = oldversion+1 where version = oldversion

悲观锁

  • 悲观锁:认为数据在同一时刻总会被修改

  • 优势:更严格的避免数据混乱问题

  • 缺点:性能消耗大

在Java中能够直接经过关键字syncrhoized来实现。syncrhoized是一种独占锁,当一个线程获取到该锁以后,其余线程就会挂起,一直等待锁释放以后,才会继续执行。这样就能保证在并发的状况下数据不会混乱。可是相应的,其对性能的损耗也较大。

  在mysql中可使用select…for update实现悲观锁。这样那条数据就被咱们锁定了,其它的事务必须等本次事务提交以后才能执行。从而保证数据不会被其余事务更改从而致使数据的异常。可是select…for update不会阻塞select的查询。

  须要注意的是mysql在采用InnoDB时,默认为行锁,且只有明确额指定主键,MySQL 才会执行行锁,锁住对应的那条数据,不然MySQL 将会执行表锁(将整个数据表单给锁住)。

相关文章
相关标签/搜索