首页 > 系统相关 >18 Java内存模型与线程_JVM同步机制和锁类库实现线程安全

18 Java内存模型与线程_JVM同步机制和锁类库实现线程安全

时间:2022-12-16 15:11:24浏览次数:73  
标签:类库 同步 Java 互斥 对象 安全 线程

目录

1 线程安全定义

含糊的定义:如果一个对象可以安全地被多个线程同时使用,那它就是线程安全的
严谨的定义:

当多个线程同时访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那就称这个对象是线程安全的。-----From《Java并发编程实战》作者Brian Goetz

2 Java数据与线程安全

从线程安全角度,将Java中各种操作共享的数据分为:不可变、绝对线程安全、相对线程安全、线程兼容和线程对立

2.1 不可变

不可变的对象一定是线程安全。

如何保证不可变呢?

  1. 基本数据类型,用final修饰
  2. 对象类型:用final修饰对象中可变的字段

Java中常用的不可变对象:String、Number的部分子类如 Long、Double、BigInteger、BigDecimal等。

为什么String不可变,参考:基本数据类型及String

2.2 绝对线程安全

ConcurrentHashMap (我后续会更新ConcurrentHashMap源码分析专题)

2.3 相对线程安全

定义:只能保证对象单次的操作是线程安全,连续调用不能保证线程安全
大部分声称线程安全的类都属于这种类型,例如Vector、HashTable、Collections的 synchronizedCollection()方法包装的集合等。

2.4 线程兼容

定义:对象本身并不是线程安全的,但是可以通过在调用端使用同步手段来保证对象在并发环境中可以安全地使用。如集合类ArrayList和HashMap等。

2.5 线程对立

定义:不管调用端是否采取了同步措施,都无法在多线程环境中并发使用代码。
例子:Thread类的suspend()和resume()方法:如果有两个线程同时持有一个线程对象,一个尝试去中断线程,一个尝试去恢复线程,在并发进行的情况下,无论调用时是否进行了同步,目标线程都存在死锁风险

3 Java线程安全支持

JVM同步机制锁类库实现线程安全

3.1 互斥同步

同步:在多个线程并发访问共享数据时,保证共享数据在同一个时刻只被一条线程使用
互斥:实现同步的一种手段。
互斥同步性能开销:互斥同步属于一种悲观的并发策略,无论共享的数据是否真的会出现竞争,它都会进行加锁,引发:用户态到核心态转换、维护锁计数器、检查是否有被阻塞的线程需要被唤醒 等开销

3.1.1 synchronized互斥同步原理

  1. synchronized关键字经过Javac编译之后,会在同步块的前后分别形成monitorenter和monitorexit这两个字节码指令。
  2. 执行monitorenter指令时,首先要去尝试获取对象的锁。如果这个对象没被锁定,或者当前线程已经持有了那个对象的锁,就把锁的计数器的值增加1
  3. 执行 monitorexit指令时会将锁计数器的值减1,一旦计数器的值为零,锁随即就被释放
  4. 如果获取对象锁失败,那当前线程阻塞等待,直到锁被释放。

关于同步块的说明:
monitorenter和monitorexit指令,都需要一个reference类型的参数,指明要锁定和解锁的对象。synchronized修饰地方不同,reference取不同的值:

  • 修饰 对象,取这个对象的引用作为reference;
  • 修饰 实例方法,取方法所属对象实例作为reference,
  • 修饰 类方法,取Class对象来作为线程要持有的锁

根据两个monitorenter和monitorexit这两个字节码指令执行过程,可以得出以下推论:

  • 被synchronized修饰的同步块对同一条线程来说是可重入
  • 被synchronized修饰的同步块在持有锁的线程执行完毕并释放锁之前,会无条件地阻塞后面其他线程的进入

3.1.2 Lock接口和ReentrantLock互斥同步原理

参考:JUC锁: LockSupport详解 JUC锁: ReentrantLock详解 这两个专题讲解

3.1.3 synchronized和Lock对比

Lock应该确保在finally块中释放锁,否则一旦同步代码块中抛出异常,则有可能永远不会释放持有的锁。Lock必须由程序员来保证锁释放,而synchronized由Java虚拟机来确保即使出现异常,锁也能被自动释放。

3.2 非阻塞同步

非阻塞同步:乐观并发策略:不管风险,先进行操作,如果没有其他线程争用共享数据,那操作就直接成功了。如果共享的数据的确被争用,产生了冲突,那再进行其他的补偿措施,最常用的补偿措施是不断地重试,直到出现没有竞争的共享数据为止。乐观并发策略的实现不再需要把线程阻塞挂起

利用处理器指令,实现非阻塞同步

硬件保证某些从语义上看起来需要多次操作的行为可以只通过一条处理器指令就能完成,这类指令常用的有:

  • 测试并设置(Test-and-Set)
  • 获取并增加(Fetch-and-Increment)
  • 交换(Swap)
  • 比较并交换(Compare-and-Swap :CAS)
  • 加载链接/条件储存(Load-Linked/Store-Conditional:LL/SC)

CAS的专题分析:JUC原子类: CAS, Unsafe和原子类详解

3.3 无同步方案

如果方法不涉及共享数据,那自然不需要采用同步措施来保证其正确性。因此会有一些代码天生就是线程安全的

3.3.1 可重入代码

代码定义:可以在代码执行的任何时刻中断它,转而去执行另外一段代码(包括递归调用它本身),而在控制权返回后,原来的程序不会出现任何错误,也不会对结果有所影响。

特征:

  • 不依赖全局变量、堆数据、公用的系统资源
  • 用到的状态量都由参数中传入
  • 不调用非可重入的方法

3.3.2 线程本地存储

代码定义:共享数据保证只在同一个线程中执行
场景:消费队列的架构模式(如“生产者-消费者”模式)都会将产品的消费过程限制在一个线程中消费完。

如果变量要被多线程访问,使用volatile关键字将它声明为“易变的”;如果变量线程独享,通过java.lang.ThreadLocal类来实现线程本地存储的功能。

ThreadLocal专题分析:Threadlocal源码解读

标签:类库,同步,Java,互斥,对象,安全,线程
From: https://www.cnblogs.com/knowledgeispower/p/16987417.html

相关文章

  • java.lang.NoClassDefFoundError: Could not initialize class sun.awt.X11Graphi
    公司项目windows迁移到linux系统,导出Excel报错误,解决办法:配置Tomcat中的catalina.sh在JAVA_OPTS中添加-Djava.awt.headless=true这样的代码,在末尾加上-Djava.awt.hea......
  • 【转载】完美解决 java: 无效的目标发行版: 11
    在使用IDEA编译程序时出现下面的错误信息:java:无效的目标发行版:11问题描述经过研究才发现,这是因为作者使用了jdk8进行编译,而试图使用jdk11的功能,这就必然会导致版本问......
  • 基于Java实现数据脱敏
    用法Jdk版本大于等于1.8maven依赖<dependency><groupId>red.zyc</groupId><artifactId>desensitization</artifactId><version>2.4.6</version></d......
  • 一文带你搞懂java中的变量的定义是什么意思
    前言在之前的文章中,壹哥给大家讲解了Java的第一个案例HelloWorld,并详细给大家介绍了Java的标识符,而且现在我们也已经知道该使用什么样的工具进行Java开发。那么接下来,壹哥......
  • 0基础→自动化测试框架实现:java + testng + httpclient + allure
    必备基础java基础配置文件解析(properties)fastjson的使用(处理json字符串、json数组)jsonpath的使用java操作excel(通过POIHttpClient的使用(get、post请求)TestNG用法 自动化测......
  • java面向对象面试题(2)
    1)给定如下java代码程序片断:      classA{             publicA(){                    System.out.println(“A”);   ......
  • Java多线程详解(通俗易懂)
    一、线程简介1.什么是进程?电脑中会有很多单独运行的程序,每个程序有一个独立的进程,而进程之间是相互独立存在的。例如图中的微信、酷狗音乐、电脑管家等等。2.什么是......
  • JAVA8自带TreeUtils
        tree.json{"code":200,"msg":"操作成功","data":[{"id":"310000","name":"电子商务","parentId":"000000"}......
  • 享誉全球的 Java 经典著作《Java核心技术》Java 17
    Java诞生27年来,这本享誉全球的Java经典著作《CoreJava》一路伴随着Java的成长,得到了百万Java开发者的青睐,几乎出现在每个“学Java要看什么书”类似的书单里,影响了......
  • 【LeetCode】题解合集(JavaScript版)
    前言:今年断断续续写了些LeetCode题目,一方面是为了一个比较现实的问题——面试,最重要的是要复习之前学习的数据结构与算法。后面有时间还会接续刷题…题解合集:题号题目题解1......