线程基础知识(三)

news2024/10/5 20:21:29

前言

之前两篇文章介绍了线程的基本概念和锁的基本知识,本文主要是学习同步机制,包括使用synchronized关键字、ReentrantLock等,了解锁的种类,死锁、竞争条件等并发编程中常见的问题。

一、关键字synchronized

  1. synchronied关键字可以把任意一个非null的对象当做锁,属于独占式的悲观锁。同时属于可重入锁
  2. 早期的的synchronized属于重量级的锁,效率低下,因为监视器是依赖底层的操作系统Lock实现的,从6之后java对sychronized进行了优化,jdk1.6以后还引入了 大量的优化,比如自旋锁,适应性锁,锁消除,锁粗化,偏向锁,轻量级锁等。

1.synchronized用法

常用来保证代码的原子性,主要有三种使用方法

  • 修饰实例:作用于当前的对象实例加锁,进入同步代码前获得,当前对象实例的锁
synchronized void method() {
//业务代码
}
  • 修饰静态方法: 也就是给当前类加锁,会作用于该类所有的对象实例。如果线程A调用一个实例对象的非静态synchronized方法,而线程B需要调用这个实例对象所属类的静态synchronized方法,是允许的,不会发生互斥现象,因为静态synchronized方法是占用的锁是当前类的锁,而访问非静态synchronized方法占用的锁是当前实例对象的锁。
synchronized void staic method() {
//业务代码
}
  • 修饰代码块: 指定加锁对象,对给定的对象/类加锁,synchronized(this object)表示进入同步前要获得给定对象的锁,synchronized(类.class)表示进入同步前要获得给定类class的锁
synchronized(this) {
//业务代码
}

2. synchronized实现原理

  • 使用synchronized是不用我们去加锁和释放lock,unlock,是jvm已经代替去做了
  • synchronized修饰代码块的时候,jvm是使用monitorenter和monitorexit两个指令实现的(监视器)
    在这里插入图片描述
  • 当修饰同步方法,jvm采用ACC_SYNCHRONIZED标记符来实现的同步, 这个标识表面了这是一个同步方法

在这里插入图片描述

3.synchronized锁住的原理

monitorenter,monitorexit,ACC_SYNCHRONIZED都是基于monitor(监视器)
所谓的Monitor其实是一种同步工具,也可以说是一种同步机制。在Java虚拟机(HotSpot)中,Monitor是由
ObjectMonitor实现的,可以叫做内部锁,或者Monitor锁。
ObjectMonitor的工作原理:
ObjectMonitor有两个队列:WaitSet、EntryList,用来保存ObjectWaiter 对象列表。
_owner,获取 Monitor 对象的线程进入 _owner 区时, _count + 1。如果线程调用了wait() 方法,此时会释放Monitor 对象, _owner 恢复为空, _count - 1。同时该等待线程进入 _WaitSet 中,等待被唤醒。
-同步是锁住的

  • monitorenter,在判断拥有同步标识 ACC_SYNCHRONIZED 抢先进入此方法的线程会优先拥有 Monitor 的owner ,此时计数器+1。
  • monitorexit,当执行完退出后,计数器-1,归 0 后被其他进入的线程获得

4.除了原子性,synchronized的可见性和有序性,可重入性怎么实现

  1. 可见性:线程加锁前,将清空工作内存中共享变量的值,从而使用共享变量的时候,需要从主内存中重新读取最新的值。线程加锁后,其他线程无法获得主内存中的共享变量的值,线程解锁前必须把共享变量的最新值刷新到主内存中。
  2. 有序性:synchronized同步的代码块具有排他性,一次只能被一个线程拥有,所以可以保证同一个时刻,代码是单线程执行的,因为as-if-serial存在,单线程语句是能够保证最终结果是有序的,但是不保证不会进行指令重排,所以synchronized是保证有序是执行结果的有序而不是防止指令重排的有序性。
  3. 可重入性:synchronized是可重入锁,也就说允许一个线程二次请求自己持有的锁的临界资源,这种情况就是可重入锁,锁对象有个计数器,会记录线程获取锁的次数,当执行完对应的代码后,计数器就会减去1,只有归零就会释放锁。之所以可以重入就是因为这个计数器。

5.synchronized和ReentrantLock的区别

可从锁的实现、功能特点、性能维度等分析

  • 锁的实现:synchronized是通过jvm实现,是java的关键字;而reentrantlock是通过jdk层面的api实现的的(一般是lock()和unlock()方法配合try/catch/finally语句实现)

  • 性能:jdk1.6前synchronized性能比较差,应该都是要通过底层调用,但是1.6以后增加了适应性自旋,锁消除等,两者性能差不多。

  • 功能特点:-

    • ReentrantLock比synchronized增加了一些高级功能,如等待中断,可实现公平锁,可实现选择性通知;
    • synchronized只能是非公平锁(内部锁),- ReentrantLock可以指定是公平还是非公平(公平锁就是先等待的线程先获得锁);
    • synchronized与wait()和notify()/notifyAll()方法结合实现等待/通知机制,ReentrantLock类借助Condition接口与newCondition()方法实现。
    • ReentrantLock需要手工声明来加锁和释放锁,一般跟finally配合释放锁。而synchronized不用手动释放锁

在这里插入图片描述

二、锁类型

锁可以分为

  • 悲观、乐观锁
  • 独享、共享锁
  • 互斥锁、读写锁
  • 可重入锁
  • 公平锁、非公平锁
  • 分段锁
  • 偏向锁,轻量级锁、重量级锁
  • 自旋锁

以上是锁的名词,有的是指锁的状态,有的是锁特性或者设计。

1.乐观锁、悲观锁

乐观锁和悲观锁并不是两种特定类型锁,是人们定义的概念或者思想。主要是指人们看待同步的角度。

  • 乐观锁:顾名思义就是乐观的认为每次取数据,别人都不会修改,所以不上锁,但是在更新的时候会去判断在此期间别人有没有取更新这个数据,可以使用版本号等机制,乐观锁适用于多读的应用程序,这样可以提高吞吐量,在java中原子变量类就是使用了乐观锁的一种实现方式CAS(compare and swap 比较并交换)来实现的
  • 悲观锁:总是假设每次去获取数据,都认为别人会修改,所以每次拿取数据都会进行上锁,这样别人拿取数据就会阻塞,直到拿到锁才行,比如Java里面的关键字synchronized实现就是悲观锁,悲观锁适合写操作多的场景

①. 乐观锁:乐观锁适合读多的场景,不加锁会代理大量的性能提升 ,在java编程中是无锁编程,常常采用的是CAS算法。典型的例子就是原子类,通过CAS自旋实现原子的更新操作。
乐观锁更新判断其他线程有没有更新共享变量 一般采用数据版本机制或者CAS操作实现
(1):数据版本机制:一般两种方式,一种是使用版本号,另一个是使用时间戳方式。
版本号方式:一般是在数据表上加上一个数据版本号version字段,表示更新的次数,当数据被更新的时候计数加一,当线程A更新数据时候,会在读取数据的同时也会读取version字段,在更新提交的时候,若刚才的读取的version和数据库中的version相等才会更新,否则会重新进行更新操作。直到更新成功。

update table set xxx=#{xxx}, version=version+1 where id=#{id} and version=#{version};

(2):CAS操作:当多个线程尝试使用CAS同时更新一个变量时候,只有一个线程能够更新变量,其他线程并不会被挂起,会收到通知失败,并可以再次尝试。
CAS需要三个字段值:1.需要读写的内存位置(V)、进行比较的预期原值(A)和拟写入的新值(B),如果内存位置的V值和预期原值A想匹配,那么就会更新B,否则不做变动。

②:悲观锁:悲观锁认为对于同一个数据的并发操作,一定会发生修改的,哪怕没有修改,也会认为修改。因此对于同一份数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁并发操作一定会出问题。在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)。如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。具体响应方式由开发者根据实际需要决定。如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。期间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常。

2.独享锁、共享锁

独享锁是指该锁只能被一个线程获取,共享锁是指该锁可以被多个线程持有。
对于java而言ReentrantLock是独享锁,但是对于另一个lock的实现类ReadWriterLock来说,其读是共享锁,其写是独占锁。独享锁和共享锁是通过AQS来实现的,通过不同的方法,来实现独享或者共享(synchronized是独占锁)

AQS:AbstractQueueSynchronized抽象同步队列,简称AQS;它是java并发包的基础,并发的锁就是基于Aqs实现的。

  • AQS是基于一个FIFO的双向队列,其内部定义了一个node节点类,node节点内部的SHARED用来标记该线程是获取共享变量时被阻挂起后放入AQS队列的,EXCLUSIVE用来标记线程是独占资源时被挂起放入AQS队列。

  • AQS使用一个volatile修饰的int类型的成员变量state来表示同步状态,修改同步状态成功表示获得锁,volatile保证了变量在线程之间的可见性,修改state通过CAS机制来保证修改的原子性。

  • 获取state方式有两种,独占和共享。一个线程使用了独占的方式,那么其他线程就失败会被阻塞;一个线程使用共享时获取资源,另一个线程还可以通过CAS的方式进行获取。

  • 如果共享资源被占用,需要一定的阻塞等待唤醒机制来保证锁的分配,AQS会将获取共享资源失败的线程添加到一个变体的CLH中。

  • 在这里插入图片描述

    AQS中的ClH变体等待队列特性

    • AQs中队列是个双链表,也是符合FIFO先进先出的特性。
    • 通过head、tail两个头尾节点来组成队列结构,通过volatile来保证可见性。
    • Head指向的节点本身已经获得了锁,是一个虚拟节点,节点本身不具备具体线程
    • 获取不到同步状态,会将节点进行自旋获取锁,自旋一定次数失败后,会将线程阻塞,相对于CLH队列性能较好。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1342120.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

android studio 将含有jni c++ 的library项目封装成jar并调用

请参考博客:android studio 4.1.1 将library项目封装成aar 并调用_android studio 4.1 aar release-CSDN博客 一 . 简单叙述 android studio 中可以创建Module 的两种属性,可以在build.gradle 中查看: 1. application属性:可以独…

2023我的编程之路

你的编程学习经历和成长过程: 在我探索编程世界的旅程中,我深刻地认识到,编程不仅仅是一种技术, 是一种思维方式, 一种解决问题的方法。希望通过分享我的编程学习体验,能够启发更多的人看到编程的魅力和价值…

分享70套超治愈的宫崎骏漫画PPT模板

又到了每年最年终总结的时候了,最近都在忙着做总结的PPT,找了好些个模板,希望这些模板也能够对你做PPT有用。 这70套唯美的宫崎骏治愈系PPT模板,适合文艺又与众不同的你。可以用在文艺汇报、工作总结、作品展示、产品介绍等。 Tip…

ssm基于javaweb的数学竞赛网站的设计与实现论文

摘 要 传统办法管理信息首先需要花费的时间比较多,其次数据出错率比较高,而且对错误的数据进行更改也比较困难,最后,检索数据费事费力。因此,在计算机上安装数学竞赛网站软件来发挥其高效地信息处理的作用&#xff0c…

【微服务核心】MyBatis Plus

MyBatis Plus 文章目录 MyBatis Plus1. 简介2. 入门使用3. 核心功能3.1 CRUD 接口3.1.1 Mapper CRUD 接口3.1.2 Service CRUD 接口 3.2 条件构造器3.3 分页插件3.4 Mybatis-Plus 注解 4. 拓展4.1 逻辑删除4.2 MybatisX快速开发插件 5. 插件5.1 [分页插件](#page)5.2 乐观锁插件…

《Python》:深拷贝、浅拷贝、赋值之间的关系(附可变与不可变)【用图文讲清楚!】

背景 想必大家面试或者平时学习经常遇到问python的深拷贝、浅拷贝和赋值之间的区别了吧?看网上的文章很多写的比较抽象,小白接收的难度有点大,于是乎也想自己整个文章出来供参考 可变与不可变 讲深拷贝和浅拷贝之前想讲讲什么是可变数据类型…

Openslide安装

文章目录 安装open-slide python下载openslide二进制文件解压到Anaconda的library目录下配置环境变量在py文件中添加以下语句即可 官网链接 安装open-slide python 表面上这样就可以导入了但事实上会遇到 Couldn’t locate OpendSlide DLL的问题,openslide必须独立安…

TikTok女性力量:短视频中女性如何重新定义自己

随着社交媒体的兴起,TikTok作为一种全新的短视频平台,为全球的用户提供了一个创作、分享和表达的空间。 在这个数字时代,女性在TikTok上展现了强大的影响力,通过短视频重新定义了自己的形象、角色和社会地位。本文将深入探讨TikT…

springcloud中使用openfeign来优化接口调用

简单介绍在springcloud中使用openfeign来优化接口调用 目录 一、引入依赖二、为服务提供者编写openfeign接口三、服务消费者调用定义的openfeign接口四、项目结构五、日志级别配置1、通过配置类进行全局配置2、通过配置类进行局部配置3、通过配置文件配置 一、引入依赖 <!-…

回顾2023,展望2024

时光飞逝&#xff0c;光阴似箭&#xff0c;转眼间又到了一年的年末&#xff0c;现在是2023年12月29日&#xff0c;再过两天就要元旦了&#xff0c;我们也要跨入2024年了。 记录自己的总结&#xff0c;一直想写&#xff0c;不知从何写起&#xff0c;在这一年中&#xff0c;有深夜…

【Redis前奏曲】初识Redis

文章目录 一.Redis的一些特性(优点)1. 在内存中存储数据2. 可编程的3. 可扩展的4.持久化5. 聚集(集群)6. 高可用Redis快的原因 二. 使用案例1.数据库2. 缓存3. 消息队列 一.Redis的一些特性(优点) 我们在上一篇博客中说到,Redis是一个在内存中存储数据的中间件.用作数据库,数据…

使用vue3实现echarts漏斗图表以及实现echarts全屏放大效果

1.首先安装echarts 安装命令&#xff1a;npm install echarts --save 2.页面引入 echarts import * as echarts from echarts; 3.代码 <template> <div id"main" :style"{ width: 400px, height: 500px }"></div> </template> …

ffmpeg两种windows版本区别说明

版本一 必须拷贝exe和dll文件才能使用&#xff0c;如果缺少dll则exe不正正常执行 如果缺少dll &#xff0c;执行 exe会报错如下 版本2 直接拷贝exe就能使用&#xff0c;没有依赖的环境

(JAVA)-(多线程)-线程池

线程池&#xff0c;顾名思义就是存放线程的池子&#xff0c;当有任务时能够随时取用线程&#xff0c;任务结束后能够放回线程池中。如果把线程比成碗&#xff0c;线程池就像一个碗柜一样。 使用线程池的好处&#xff1a; 1.当有大量线程对象时&#xff0c;减少了线程创建销毁…

浅谈直流电表在韩国充电桩生产厂家的应用

I.背景&#xff1a; 近几年为应对温室气体的排放导致的全球变暖、气候变化等问题,各大国纷纷对焦推进电动汽车&#xff0c;从而减少传统燃油汽车带来的大量温室气体排放。而推进新能源汽车的各项举措之中&#xff0c;充电桩的基础建设&#xff0c;又是其中的重中之重&#xff…

【MySQL】数据库并发控制:悲观锁与乐观锁的深入解析

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a; 数 据 库 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 悲观锁&#xff08;Pessimistic Locking&#xff09;: 乐观锁&#xff08;Optimistic Locking&#xff09;: 总结&#x…

2023年,我46岁,进入关机模式,稳了

关键词&#xff1a;持续关机、稳住基本盘、力量训练、新开端。 持续关机 对于性格内向的我&#xff0c;过上安静的生活才是美好岁月的真正开端。我尽量减少不必要的活动&#xff0c;能不出门就不出门。 在10月份&#xff0c;我停止了日更栏目“大志聊赚钱”&#xff0c;该栏目…

紧固件行业的市场规模和增长率是怎样的,主要市场区域有哪些?

分析标准件行业市场容量和增长率 标准件指的是在各种工业设备中广泛使用的连接件&#xff0c;通常由螺栓、螺帽、垫圈等组成。它能够将两个或两个以上的零件牢固地固定在一起&#xff0c;起到传递力量或者关闭效果的作用。标准件是机械制造、汽车、航空、电子、建筑等行业中不…

InterSystems 数据库的存储过程存在哪里

我们都知道 InterSystems 的 Studio 可以创建存储过程。 但这个存储过程我们保存的时候是保存在哪里&#xff1f; 存储逻辑 如果我们在 Studio 创建存储过程的话&#xff0c;存储过程是存储在数据库上面的。 本地文件夹中是没有存储的。 选择系统下面的存储过程&#xff0c…

PostgreSQL10数据库源码安装及plpython2u、uuid-ossp插件安装

PostgreSQL10数据库源码安装及plpython2u、uuid-ossp插件安装 1、环境2、安装包下载3、安装3.1 、解压3.2、配置3.3、编译安装3.4 、启动与关闭 4、安装 uuid-ossp 、plpython2u插件5、参考 1、环境 centos 7 、 postgresql 10.19 2、安装包下载 postgres 源码安装包 3、安…