我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java并发编程中的悲观锁和乐观锁机制

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java并发编程中的悲观锁和乐观锁机制

这篇文章主要介绍“Java并发编程中的悲观锁和乐观锁机制”,在日常操作中,相信很多人在Java并发编程中的悲观锁和乐观锁机制问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java并发编程中的悲观锁和乐观锁机制”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

一、资源和加锁

1、场景描述

多线程并发访问同一个资源问题,假如线程A获取变量之后修改变量值,线程C在此时也获取变量值并且修改,两个线程同时并发处理一个变量,就会导致并发问题。

Java并发编程中的悲观锁和乐观锁机制

这种并行处理数据库的情况在实际的业务开发中很常见,两个线程先后修改数据库的值,导致数据有问题,该问题复现的概率不大,处理的时候需要对整个模块体系有概念,才能容易定位问题。

2、演示案例

public class LockThread01 {    public static void main(String[] args) {        CountAdd countAdd = new CountAdd() ;        AddThread01 addThread01 = new AddThread01(countAdd) ;        addThread01.start();        AddThread02 varThread02 = new AddThread02(countAdd) ;        varThread02.start();    }}class AddThread01 extends Thread {    private CountAdd countAdd  ;    public AddThread01 (CountAdd countAdd){        this.countAdd = countAdd ;    }    @Override    public void run() {        countAdd.countAdd(30);    }}class AddThread02 extends Thread {    private CountAdd countAdd  ;    public AddThread02 (CountAdd countAdd){        this.countAdd = countAdd ;    }    @Override    public void run() {        countAdd.countAdd(10);    }}class CountAdd {    private Integer count = 0 ;    public void countAdd (Integer num){        try {            if (num == 30){                count = count + 50 ;                Thread.sleep(3000);            } else {                count = count + num ;            }            System.out.println("num="+num+";count="+count);        } catch (Exception e){            e.printStackTrace();        }    }}

这里案例演示多线程并发修改count值,导致和预期不一致的结果,这是多线程并发下最常见的问题,尤其是在并发更新数据时。

出现并发的情况时,就需要通过一定的方式或策略来控制在并发情况下数据读写的准确性,这被称为并发控制,实现并发控制手段也很多,最常见的方式是资源加锁,还有一种简单的实现策略:修改数据前读取数据,修改的时候加入限制条件,保证修改的内容在此期间没有被修改。

二、锁的概念简介

1、锁机制简介

并发编程中一个最关键的问题,多线程并发处理同一个资源,防止资源使用的冲突一个关键解决方法,就是在资源上加锁:多线程序列化访问。锁是用来控制多个线程访问共享资源的方式,锁机制能够让共享资源在任意给定时刻只有一个线程任务访问,实现线程任务的同步互斥,这是最理想但性能最差的方式,共享读锁的机制允许多任务并发访问资源。

2、悲观锁

悲观锁,总是假设每次每次被读取的数据会被修改,所以要给读取的数据加锁,具有强烈的资源独占和排他特性,在整个数据处理过程中,将数据处于锁定状态,例如synchronized关键字的实现就是悲观机制。

Java并发编程中的悲观锁和乐观锁机制

悲观锁的实现,往往依靠数据库提供的锁机制,只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据,悲观锁主要分为共享读锁和排他写锁。

排他锁基本机制:又称写锁,允许获取排他锁的事务更新数据,阻止其他事务取得相同的资源的共享读锁和排他锁。若事务T对数据对象A加上写锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的写锁。

3、乐观锁

乐观锁相对悲观锁而言,采用更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务的开销非常的占资源,乐观锁机制在一定程度上解决了这个问题。

Java并发编程中的悲观锁和乐观锁机制

乐观锁大多是基于数据版本记录机制实现,为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个version字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号等于数据库表当前版本号,则予以更新,否则认为是过期数据。乐观锁机制在高并发场景下,可能会导致大量更新失败的操作。

乐观锁的实现是策略层面的实现:CAS(Compare-And-Swap)。当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能成功更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。

4、机制对比

悲观锁本身的实现机制就以损失性能为代价,多线程争抢,加锁、释放锁会导致比较多的上下文切换和调度延时,加锁的机制会产生额外的开销,还有增加产生死锁的概率,引发性能问题。

乐观锁虽然会基于对比检测的手段判断更新的数据是否有变化,但是不确定数据是否变化完成,例如线程1读取的数据是A1,但是线程2操作A1的值变化为A2,然后再次变化为A1,这样线程1的任务是没有感知的。

悲观锁每一次数据修改都要上锁,效率低,写数据失败的概率比较低,比较适合用在写多读少场景。

乐观锁并未真正加锁,效率高,写数据失败的概率比较高,容易发生业务形异常,比较适合用在读多写少场景。

是选择牺牲性能,还是追求效率,要根据业务场景判断,这种选择需要依赖经验判断,不过随着技术迭代,数据库的效率提升,集群模式的出现,性能和效率还是可以两全的。

三、Lock基础案例

1、Lock方法说明

lock:执行一次获取锁,获取后立即返回;

lockInterruptibly:在获取锁的过程中可以中断;

tryLock:尝试非阻塞获取锁,可以设置超时时间,如果获取成功返回true,有利于线程的状态监控;

unlock:释放锁,清理线程状态;

newCondition:获取等待通知组件,和当前锁绑定;

2、应用案例

import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class LockThread02 {    public static void main(String[] args) {        LockNum lockNum = new LockNum() ;        LockThread lockThread1 = new LockThread(lockNum,"TH1");        LockThread lockThread2 = new LockThread(lockNum,"TH2");        LockThread lockThread3 = new LockThread(lockNum,"TH3");        lockThread1.start();        lockThread2.start();        lockThread3.start();    }}class LockNum {    private Lock lock = new ReentrantLock() ;    public void getNum (){        lock.lock();        try {            for (int i = 0 ; i < 3 ; i++){                System.out.println("ThreadName:"+Thread.currentThread().getName()+";i="+i);            }        } finally {            lock.unlock();        }    }}class LockThread extends Thread {    private LockNum lockNum ;    public LockThread (LockNum lockNum,String name){        this.lockNum = lockNum ;        super.setName(name);    }    @Override    public void run() {        lockNum.getNum();    }}

这里多线程基于Lock锁机制,分别依次执行任务,这是Lock的基础用法,各种API的详解,下次再说。

3、与synchronized对比

基于synchronized实现的锁机制,安全性很高,但是一旦线程失败,直接抛出异常,没有清理线程状态的机会。显式的使用Lock语法,可以在finally语句中最终释放锁,维护相对正常的线程状态,在获取锁的过程中,可以尝试获取,或者尝试获取锁一段时间。

四、源代码地址

GitHub·地址https://github.com/cicadasmile/java-base-parentGitEE·地址https://gitee.com/cicadasmile/java-base-parent

到此,关于“Java并发编程中的悲观锁和乐观锁机制”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Java并发编程中的悲观锁和乐观锁机制

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java并发编程的悲观锁和乐观锁机制

本篇内容主要讲解“Java并发编程的悲观锁和乐观锁机制”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java并发编程的悲观锁和乐观锁机制”吧!一、资源和加锁1、场景描述多线程并发访问同一个资源问
2023-06-16

Java并发编程中的悲观锁和乐观锁机制

这篇文章主要介绍“Java并发编程中的悲观锁和乐观锁机制”,在日常操作中,相信很多人在Java并发编程中的悲观锁和乐观锁机制问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java并发编程中的悲观锁和乐观锁机制
2023-06-02

Java悲观锁和乐观锁机制是什么

本篇内容主要讲解“Java悲观锁和乐观锁机制是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java悲观锁和乐观锁机制是什么”吧!1、锁机制简介并发编程中一个最关键的问题,多线程并发处理同一
2023-06-02

浅谈一下Java中的悲观锁和乐观锁

这篇文章主要介绍了一下Java中的悲观锁和乐观锁,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-05-16

java中乐观锁与悲观锁的概念

这篇文章主要介绍“java中乐观锁与悲观锁的概念”,在日常操作中,相信很多人在java中乐观锁与悲观锁的概念问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”java中乐观锁与悲观锁的概念”的疑惑有所帮助!接下来
2023-06-19

Java中的乐观锁和悲观锁简单介绍

这篇文章主要介绍“Java中的乐观锁和悲观锁简单介绍”,在日常操作中,相信很多人在Java中的乐观锁和悲观锁简单介绍问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java中的乐观锁和悲观锁简单介绍”的疑惑有所
2023-06-02

java中悲观锁和乐观锁有什么区别

本篇文章给大家分享的是有关java中悲观锁和乐观锁有什么区别,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。Java可以用来干什么Java主要应用于:1. web开发;2. An
2023-06-14

Java并发问题之乐观锁与悲观锁的示例分析

这篇文章将为大家详细讲解有关Java并发问题之乐观锁与悲观锁的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。首先介绍一些乐观锁与悲观锁:悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修
2023-05-30

Java中的悲观锁与乐观锁怎么掌握

要掌握Java中的悲观锁和乐观锁,可以遵循以下步骤:1. 了解悲观锁和乐观锁的概念:- 悲观锁(Pessimistic Locking)假设在整个事务过程中,其他线程可能会对数据进行修改,因此在访问数据之前,先获取锁并阻塞其他线程的访问。-
2023-08-12

MySQL中的乐观锁,悲观锁和MVCC全面解析

前言在数据库的实际使用过程中,我们常常会遇到不希望数据被同时写或者读的情景,例如秒杀场景下,两个请求同时读到系统还有库存1个,然后又先后把库存更新为0,这时候就会出现超卖的情况,这时候货物的实际库存和我们的记录就会对应不上了。 为了解决这种
2022-05-19

NoSQL中乐观并发控制和悲观并发控制的区别是什么

在NoSQL数据库中,乐观并发控制和悲观并发控制是两种常见的并发控制策略。乐观并发控制:乐观并发控制假设冲突的概率较低,并允许多个事务同时对数据进行读写操作。当一个事务要修改数据时,先读取数据并记录版本号或时间戳,然后在写入时检查是否有其
NoSQL中乐观并发控制和悲观并发控制的区别是什么
2024-05-07

如何理解Java并发下的乐观锁

本篇内容主要讲解“如何理解Java并发下的乐观锁”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何理解Java并发下的乐观锁”吧!在聊乐观锁之前,先给大家复习一个概念:原子操作:什么是原子操作呢
2023-06-15

redis中事务机制及乐观锁的实现

Redis事务机制在MySQL等其他数据库中,事务表示的是一组动作,这组动作要么全部执行,要么全部不执行。 Redis目前对事物的支持相对简单。Redis只能保证一个client发起的事务中的命令可以连续的执行,而中间不会插入其他的clie
2022-06-04

Java并发编程之显式锁机制详解

我们之前介绍过synchronized关键字实现程序的原子性操作,它的内部也是一种加锁和解锁机制,是一种声明式的编程方式,我们只需要对方法或者代码块进行声明,Java内部帮我们在调用方法之前和结束时加锁和解锁。而我们本篇将要
2023-05-30

Python并发编程中的锁机制,揭秘并发编程中的同步之道

Python并发编程中,锁机制是实现线程安全和数据同步的有效手段。通过对不同锁的特性和应用场景的深入解析,掌握锁的正确使用技巧,可以有效地提高并发程序的性能和可靠性。
Python并发编程中的锁机制,揭秘并发编程中的同步之道
2024-02-05

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录