目录

1、乐观锁&悲观锁

1.1 乐观锁的定义

1.2 乐观锁的实现(CAS机制)

1.3 乐观锁存在的问题:ABA问题

1.4 悲观锁

2、公平锁&非公平锁

3、读写锁

4、独占锁&共享锁

5、可重入锁&自旋锁


1、乐观锁&悲观锁

1.1 乐观锁的定义

乐观锁认为一般情况下不会出现冲突,所以只会在更新数据的时候才对冲突进行数据检测,如果没有发生冲突则直接修改,如果发生冲突则不做任何修改,然后把结果返回给用户,让用户自行处理。

1.2 乐观锁的实现(CAS机制)

CAS(COMPARE AND SWAP):对比替换

V:内存中的值

A:预期的旧值

B:要替换的新值

CAS实现机制:比较V和A是否相等,相等则将V更改为B,否则提示修改失败。核心:先比较再替换

CAS实现借助Unsafe类,Unsafe类调用操作系统的Atomic::cmpxchg(原子性汇编指令)

我们一般不用Unsafe类,因为其中的方法可以直接对内存进行操作,是不安全的。

我们推荐使用Atomicxxx来实现CAS机制保证线程安全。如下:

import java.util.concurrent.atomic.AtomicInteger;/*** CAS 使用*/
public class CASDemo1 {private static int number = 0;private static AtomicInteger atomicInteger = new AtomicInteger(0);private final static int MAX_COUNT = 100000;public static void main(String[] args) throws InterruptedException {// ++Thread t1 = new Thread(() -> {for (int i = 0; i < MAX_COUNT; i++) {atomicInteger.getAndIncrement(); // i++
//                number++;}});t1.start();// --Thread t2 = new Thread(() -> {for (int i = 0; i < MAX_COUNT; i++) {atomicInteger.getAndDecrement(); // i--
//                number--;}});t2.start();t1.join();t2.join();System.out.println("最终结果:" + atomicInteger.get());}
}

1.3 乐观锁存在的问题:ABA问题

ABA问题代码演示:

import java.util.concurrent.atomic.AtomicInteger;/*** CAS ABA 演示*/
public class ABADemo1 {private static AtomicInteger money = new AtomicInteger(100);public static void main(String[] args) throws InterruptedException {//第一次点击转账(-50)Thread t1 = new Thread(()->{int old_money = money.get();try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}money.compareAndSet(old_money,old_money+50);});t1.start();//第二次点击转账(-50)[不小心点击的,因为第一次点击没有反应,所以又点了一次]Thread t2 = new Thread(()->{int old_money = money.get();money.compareAndSet(old_money,old_money+50);});t2.start();//给账户+50元Thread t3 = new Thread(()->{//执行花费1stry {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}int old_money = money.get();money.compareAndSet(old_money,old_money+50);});t1.join();t2.join();t3.join();System.out.println("最终账户余额:"+money.get());}
}

ABA解决方案:

引入版本号,每次操作之后让版本号+1,执行下一步操作前,判断值和版本号,如果二者都为true才进行操作。

import java.util.concurrent.atomic.AtomicStampedReference;/*** ABA 问题演示*/
public class ABADemo2 {private static AtomicStampedReference<Integer> money =new AtomicStampedReference<>(100, 0);public static void main(String[] args) throws InterruptedException {// 第 1 次点击转账按钮(-50)Thread t1 = new Thread(() -> {int old_money = money.getReference(); // 先得到余额int version = money.getStamp(); // 得到版本号// 执行花费 2stry {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}money.compareAndSet(old_money, old_money - 50, version, version + 1);});t1.start();// 第 2 次点击转账按钮(-50)【不小心点击的,因为第一次点击之后没反应,所以不小心又点了一次】Thread t2 = new Thread(() -> {int old_money = money.getReference(); // 先得到余额int version = money.getStamp(); // 得到版本号money.compareAndSet(old_money, old_money - 50,version, version + 1);});t2.start();// 给账户 +50 元Thread t3 = new Thread(() -> {// 执行花费 1stry {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}int old_money = money.getReference();int version = money.getStamp();money.compareAndSet(old_money, old_money + 50,version, version + 1);});t3.start();t1.join();t2.join();t3.join();System.out.println("最终账号余额:" + money.getReference());}
}

1.4 悲观锁

定义:总是假设最坏的情况,每次去拿数据的时候就认为别人会修改,所以每次进行访问都要进行上锁,这样被人拿数据时就会阻塞直到它拿到锁。

应用:synchronized、lock 都是悲观锁

2、公平锁&非公平锁

非公平锁:抢占式执行,有一些先来的任务还在排队,刚好释放锁的时候新来了一个任务,此时并不会通知任务队列来执行任务,而是执行新来的任务。

公平锁:所有任务来了之后先排队,线程空闲之后去任务队列按顺序执行最早任务。(排队做核酸)

3、读写锁

读写锁 (Readers-Writer Lock)顾名思义就是把一把锁分为两个部分:读锁和写锁,读锁允许多个线程同时获得,因为读本身就是线程安全的,而写锁是互斥锁,不允许多个线程同时获得,并且读写也是互斥的。ALL IN ALL :读读不互斥、读写互斥、写写互斥。

JAVA标准库提供了ReentrantReadWriteLock类,实现了读写锁

读写锁适合于“频繁读,不频繁写”的场景

import java.time.LocalDateTime;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;/*** 演示读写锁的使用*/
public class ReadWriteLockDemo {public static void main(String[] args) {//创建读写锁final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();//创建读锁final ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();//创建写锁final ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();//创建线程池ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5,5,0,TimeUnit.SECONDS,new LinkedBlockingQueue<>());//线程池执行任务1【读操作】threadPool.submit(()->{//加锁操作readLock.lock();try {System.out.println("执行读锁1:"+ LocalDateTime.now());TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}finally {readLock.unlock();}});//线程池执行任务2【读操作】threadPool.submit(()->{//加锁操作readLock.lock();try {System.out.println("执行读锁2:"+ LocalDateTime.now());TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}finally {readLock.unlock();}});//线程池执行任务3【写操作】threadPool.submit(()->{//加锁操作writeLock.lock();try {System.out.println("执行写锁1:"+ LocalDateTime.now());TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}finally {writeLock.unlock();}});//线程池执行任务4【写操作】threadPool.submit(()->{//加锁操作writeLock.lock();try {System.out.println("执行写锁2:"+ LocalDateTime.now());TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}finally {writeLock.unlock();}});}
}

由此可看出读操作是同时进行的,而写操作则不能同时操作

4、独占锁&共享锁

独占锁:任何时候只有一个线程能执行资源操作:synchronized、lock

共享锁:可以同时被多个线程读取,但只能被一个线程修改。比如java中的读写锁。读锁可以同时被多个线程读取,而写锁则只能被一个线程修改。

5、可重入锁&自旋锁

可重入锁指的是该线程获取了该锁之后,可以无限次的进入该锁锁住的代码。

自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。

常见锁策略—乐观锁悲观锁相关推荐

  1. 什么是乐观锁,什么是悲观锁

    一.并发控制 当程序中可能出现并发的情况时,就需要通过一定的手段来保证在并发情况下数据的准确性,通过这种手段保证了当前用户和其他用户一起操作时,所得到的结果和他单独操作时的结果是一样的.这种手段就叫做 ...

  2. mysql乐观和悲观锁实现_mysql实现乐观锁和悲观锁该怎么编写?

    乐观锁和悲观锁相信大家都是知道的,这是java中的基础知识,今天我们就来看看它们两者该如何使用代码实现吧. 乐观锁实现 1).表设计 表task,分别有三个字段id,value.version 2). ...

  3. mysql proxy 悲观锁_使用MySQL悲观锁解决电商扣库存并发问题

    昨天有人提出想看悲观锁,所以今天我们就说一说如何抗悲观锁解决并发问题: 悲观锁介绍(百科): 悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态 ...

  4. 【剧前爆米花--爪哇岛寻宝】常见的锁策略——乐观锁、读写锁、重量级锁、自旋锁、公平锁、可重入锁等

    作者:困了电视剧 专栏:<JavaEE初阶> 文章分布:这是关于操作系统锁策略的文章,包括乐观锁.读写锁.重量级锁.自旋锁.公平锁.可重入锁等,希望对你有所帮助! 目录 乐观锁和悲观锁 悲 ...

  5. 多线程与高并发基础一(超发--多线程悲观锁,乐观锁、类数据库悲观锁乐观锁)

    PS:看完文章后对自己以前所做过的并发和锁机制有了深入原理的了解. 知其然和知其所以然! 遂以记之! 关键词: 线程,同步,单例,高并发,高访问,死锁 一.大规模并发带来的挑战 在过去的工作中,我曾经 ...

  6. 乐观锁和悲观锁_什么是悲观锁和乐观锁?

    思维导图 文章已收录Github精选,欢迎Star:https://github.com/yehongzhi/learningSummary 悲观锁 悲观锁是平时开发中经常用到的一种锁,比如Reent ...

  7. mysql 乐观锁和悲观锁,MySQL中的悲观锁与乐观锁

    悲观锁与乐观锁是解决资源并发场景的解决方案 CREATE TABLE `order_stock` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID' ...

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

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

  9. 什么是乐观锁,什么是悲观锁?

    在互联网公司面试中,很多小伙伴都被问到关于锁的理解.今天,我给小伙伴们来聊一聊我对锁的理解,不管我们互斥锁.自旋锁.重入锁.读写锁.行锁.表锁等等等等这些概念,我把他们都归纳为两种类型,乐观锁和悲观锁 ...

最新文章

  1. android 多个占位符,Android多语言支持:由于占位符计数不同导致的字符串格式问题...
  2. css(float浮动和clear清除)
  3. 产业链布局优势明显,三星开启全新移动智能体验新时代
  4. java单例代码_java中的单例模式的代码怎么写
  5. 关于ByteBuffer使用解释
  6. 个性化推荐认知之----数字化转型浪潮下,产品经理应如何重新认知个性化推荐?...
  7. SCOM 2012 SP1安装过程
  8. 左传 —— 春秋左氏传
  9. Flink on K8s 在京东的持续优化实践
  10. 半自动化运维之服务器信息维护
  11. CSS 相邻选择器(七)
  12. 人机大战历程————思考与反思
  13. 红包算法 递归 php,【杂谈】PHP递归算法(二)
  14. 第三方app实现微信登录功能
  15. OPNsense用户手册-硬件要求
  16. 徐亦达老师机器学习课程
  17. Linux系统学习方法
  18. python将图片合成视频
  19. 2016全球与中国市场开关插座深度研究报告
  20. 通用企业智能制造ERP源码 制造业通用ERP系统源码 工厂ERP源码C# web ASP.NET 源码

热门文章

  1. 原来还有这种操作,libs和jniLibs 还有jni不得不说的关系
  2. LEADTOOLS v18跨平台技术解析
  3. 【无标题】大数据建模、分析、挖掘技术应用
  4. cmd安装Chocolatey
  5. python 四维绘图_python四维绘图
  6. PLSQL/Oracle解决中文乱码问题
  7. 使用RT Thread设备框架封装一个I2C设备——DS3231
  8. linux删除文件或文件夹
  9. python统计英文句子每个单词字数_Python实现的统计文章单词次数功能示例
  10. php 给常量赋值,PHP入坑之 赋值、变量和常量