【Java】面试题 并发安全 (1)

文章目录

  • synchronized
      • 1. 基本概念与作用
      • 2. 使用方式
      • 3. 底层原理(基于monitor)
      • 4. 面试题回答要点
      • 5. 注意事项与进阶理解
  • Java中synchronized锁相关知识总结
  • Java内存模型(JMM)
  • CAS概述及重要性
  • volatile关键字
  • AQS简介

synchronized

在Java编程中,synchronized关键字是处理多线程并发访问共享资源的重要机制,它确保在同一时刻,只有一个线程能够访问被synchronized修饰的代码块或方法,从而保证了数据的一致性和完整性。以下是对synchronized的详细解析:

1. 基本概念与作用

  • 互斥性:synchronized保证了在同一时刻,最多只有一个线程可以获取到对象锁,进而执行被其修饰的代码块或方法。其他试图获取该锁的线程将被阻塞,直到持有锁的线程释放锁。
  • 可见性:当一个线程修改了被synchronized修饰的共享变量后,其他线程在获取锁后能够立即看到该变量的最新值。这是因为synchronized会在释放锁之前,将对变量的修改刷新到主存中,保证了变量在多个线程间的可见性。

2. 使用方式

  • 修饰代码块:可以为一段特定的代码块添加锁,语法为synchronized (对象锁) { // 需要同步的代码块 }。例如,在上述抢票代码中,synchronized (objectLock) {... }确保了在同一时刻只有一个线程能够执行getTicket方法中的抢票逻辑,防止超卖现象。
  • 修饰方法:直接修饰整个方法,表示整个方法体都需要同步。当一个线程访问该方法时,其他线程必须等待该线程执行完方法并释放锁后才能访问。

3. 底层原理(基于monitor)

在这里插入图片描述

  • 字节码层面体现:通过分析字节码信息,可以看到在使用synchronized时,会出现monitor enter(表示上锁)和monitor exit(表示解锁)指令。存在两个monitor exit是因为在底层使用了隐式的try finally结构,确保在代码执行过程中,即使发生异常,也能正确释放对象锁,避免其他线程无法获取锁而导致死锁等问题。

在这里插入图片描述

  • monitor的结构与属性
    • owner:关联当前持有锁的线程,同一时刻只能有一个线程与owner关联。当线程进入synchronized代码块时,会尝试将对象锁与monitor关联,并判断owner是否为none,如果是则该线程成为owner,即获得锁。
    • entry list:用于存放等待获取锁的线程。当一个线程尝试获取锁但owner不为none时,该线程会进入entry list并处于阻塞状态。当持有锁的线程释放锁后,会唤醒entry list中的线程来争抢锁的拥有权,这些线程之间是无序竞争的,并非先来后到顺序获取锁。
    • wait set:当线程调用了wait方法后,会进入wait set并释放锁,处于等待状态。直到其他线程调用notifynotifyAll方法,唤醒wait set中的线程,被唤醒的线程会重新竞争锁,竞争成功后才能继续执行。

4. 面试题回答要点

  • 阐述作用:明确说明synchronized采用互斥方式,确保同一时刻只有一个线程拥有对象锁,保证共享资源在多线程环境下的安全访问。
  • 解释底层实现:指出其底层由monitor实现,monitor是JVM级别的机制,获取锁需要使用对象锁关联monitor。
  • 描述monitor属性:详细解释monitor的三个属性owner、entry list和wait set的作用和关联关系,如owner关联持有锁的线程且只能有一个,entry list关联阻塞状态线程,wait set关联等待状态线程。

5. 注意事项与进阶理解

  • 性能影响:虽然synchronized保证了线程安全,但过度使用可能会导致性能问题,因为获取和释放锁需要一定的开销。在实际应用中,需要根据具体场景权衡是否使用synchronized以及如何优化锁的使用。
  • 可重入性:synchronized是可重入锁,即同一个线程可以多次获取同一把锁。例如,一个线程在已经获得对象锁的情况下,再次进入被该对象锁修饰的代码块或方法时,不会被阻塞。
  • 与其他并发机制的结合:在复杂的多线程编程中,可能需要结合其他并发机制,如volatile关键字、java.util.concurrent包下的工具类等,来更高效地实现并发控制和线程安全。例如,volatile可以保证变量的可见性,但不能保证原子性,与synchronized结合使用可以在某些场景下提高性能。
  1. 面试题回答要点
    • 回答synchronized原理面试题时,先说作用是采用互斥方式,同一时刻只能让一个线程拥有对象锁;底层由monitor实现,monitor是JVM级别的,获取锁需用对象锁关联monitor,并解释monitor的三个属性,owner关联持有锁的线程且只能关联一个,entry list关联阻塞状态线程,wait set关联等待状态线程。

Java中synchronized锁相关知识总结

  1. monitor实现重量级锁的原因

    • monitor是JVM提供的C++实现的锁,线程获取锁时需关联monitor,涉及用户态和内核态切换,资源权限不同,切换成本高,且存在进程上下文切换,性能低,因此被称为重量级锁。
  2. JDK1.6引入偏向锁和轻量级锁的原因

    • 当一个线程重复获取锁且无竞争时,若一直使用重量级锁monitor,性能低,所以引入偏向锁。例如,一个线程多次进入同一个带锁方法时,偏向锁可提升性能。
    • 当多个线程交替获取锁且无竞争时,使用重量级锁monitor性能不高,因此引入轻量级锁。
  3. 对象锁与monitor的关联方式
    在这里插入图片描述

    • Java对象在堆中,其内存结构包括对象头、实例数据和对齐填充数据。对象头的mark word记录了对象与monitor的关联信息。
    • 在32位虚拟机中,mark word不同状态下存储内容不同。无锁状态下包含哈希值、分代年龄、偏向锁标识和锁标识;偏向锁状态下记录线程id、偏向锁时间戳等;轻量级锁占用30位记录相关信息;重量级锁则指向monitor对象。对象通过在mark word中记录monitor的地址与monitor关联。
  4. 轻量级锁的工作原理
    在这里插入图片描述

    • 适用于同步代码块无竞争或多线程交替执行的情况。线程进入同步代码块时,创建锁记录,通过CAS操作将对象头的mark word与锁记录交换,若成功,对象头存储锁记录地址,表示该线程拥有锁;若失败,可能是多线程竞争(升级为重量级锁)或锁重入(添加锁记录作为重入计数)。
    • 退出同步代码块时,若锁记录为null,表示有重入,重置锁记录(计数减一);若不为null,则再次通过CAS将mark word换回原来的值解锁。
    • CAS(Compare and Swap),即比较并交换,是一种乐观锁策略,用于实现多线程环境下的同步和并发控制,它通过原子性的比较和交换操作来保证数据的一致性。
  5. 偏向锁的执行过程

    • JDK1.6后引入,适用于长时间只有一个线程使用锁的情况。线程第一次获取锁时,通过CAS操作将线程id写入对象头的mark word并设置偏向锁标识,后续该线程再次获取锁只需判断mark word中的线程id是否为自己,无需再次CAS操作,性能较好。例如,一个线程连续多次进入多个带锁且相互调用的方法时,偏向锁可提升性能。
  6. 三种锁的对比与面试题解答

    • 对比
      • 重量级锁基于monitor实现,性能最低,涉及用户态和内核态切换及上下文切换。
      • 轻量级锁在无竞争时通过修改对象头锁标志实现,性能较好,但每次添加锁记录需CAS操作保证原子性。
      • 偏向锁适用于单线程多次获取锁的场景,性能最佳,第一次获取锁后再次获取只需简单判断。
    • 面试题解答
      • synchronized中有偏向锁、轻量级锁和重量级锁。偏向锁只有一个线程持有,轻量级锁不同线程交替持有,多线程竞争时则为重量级锁。锁发生竞争时会升级为重量级锁。

在这里插入图片描述


Java内存模型(JMM)

  1. 面试题引入

    • 问题:谈一谈Java内存模型(JMM)。
    • 注意事项:JMM指Java Memory Model,不要与Java内存结构混淆,答题前需确认题意,不确定可询问面试官。
  2. Java内存模型(JMM)概念

    • 定义:共享变量中多线程程序读写操作的行为规范,通过规范内存读写保证指令正确性。
    • 共享内存示例:Java代码中的成员变量、创建的对象、数组等。
      在这里插入图片描述
  3. 内存划分

    • 工作内存
      • 分配:每个线程创建时分配一个工作内存。
      • 用途:存储线程内私有数据。
      • 访问限制:线程只能访问自己的工作内存,工作内存中的数据对每个线程私有,不存在线程安全问题。
    • 主内存
      • 性质:存储共享变量,是共享区域。
      • 共享变量内容:Java实例对象、成员变量、数组等。
      • 线程访问:每个线程都能访问主内存中的共享变量,可能存在线程安全问题。
      • 数据同步:多线程同步数据需通过主内存。
  4. 回答面试题要点

    • JMM概念:Java内存模型,定义共享内存中多线程程序读写行为规范,保证指令正确性。
    • 内存划分:分为线程私有的工作内存和所有线程可访问的主内存中的共享内存。
    • 线程交互:线程间相互隔离,交互需通过主内存共享数据。

CAS概述及重要性

  • 定义与思想:CAS全称compare and swap,即比较再交换,体现乐观锁思想,在无锁状态下保证线程操作共享数据的原子性。很多底层框架(如AQS框架、以automa开头的类等)以及之前讲过的轻量级锁和偏向锁都用到了CAS。

在这里插入图片描述

  • 操作流程
    • 主内存有共享变量(如int a = 100),线程操作前需将其读入各自工作内存。
    • 线程a对数据操作(如a++)后想同步到主内存,需拿自己工作内存中的旧预期值a与主内存中的当前值v比对,若相同则将更新后的值b赋给主内存中的v;若不同则失败并自旋(while循环)
    • 线程b同理,若比对失败则自旋,自旋即不断重新读取共享变量数据、操作后再次比对,直到成功或达到设定阈值。
  • 自旋锁特点
    • 优势是线程不会阻塞,效率较高。
    • 劣势是线程竞争激烈时,若多次循环都不能替换成功,效率会降低,所以有时会设置自旋次数阈值。
  • 底层实现:依赖unsafe类,直接调用操作系统底层的CAS指令,unsafe类中的compare and swap object、int、long三个方法(均为native修饰,由系统提供,用C或C++语言实现)用于完成CAS操作。
    在这里插入图片描述
  1. 乐观锁与悲观锁区别
    • 乐观锁(CAS):基于乐观锁思想,认为在大多数情况下,共享变量不会被其他线程修改,所以不会像悲观锁那样提前加锁,减少了锁的竞争和线程阻塞,提高了并发性能。若被修改则自旋重试,通过不断重试最终修改共享变量。
    • 悲观锁(如Synchronized):基于悲观锁思想,要防着其他线程修改共享变量,上锁后其他线程无法修改,直到解锁后其他线程才有机会操作。

volatile关键字

  1. volatile关键字介绍及问题引出

    • 关键字特性:可修饰类的成员变量或静态成员变量,被修饰后具备保证线程间可见性禁止指令重排序两层含义。
  2. volatile保证线程间可见性分析
    在这里插入图片描述

    • 代码示例逻辑:代码中有共享变量stop(初始值为false),线程一在100ms后将stop改为true并打印,线程二在200ms后获取stop的值并打印,线程三有一个以取反stop为条件的循环。
  3. 代码执行结果及问题分析

    • 测试过程:先执行线程一和线程二,结果显示线程一将stop改为true后,线程二能获取到新值并打印true,证明线程一和线程二之间共享变量可见。然后放开线程三再次执行,发现线程一修改了stop为true,但线程三的循环未结束,说明线程三没有读到stop的新值。
    • 原因分析:JVM中的即时编译器(JIT)对代码进行了优化,将取反stop的条件优化为固定的true,导致即使stop被修改,优化后的代码也无法读到新值。

在这里插入图片描述

  1. 解决方案介绍及测试
    • 第一种方案:加vm参数禁用即时编译器(JIT),添加“-Xint”参数。但此方案不推荐,因为会影响其他程序使用即时编译器。
    • 第二种方案:在共享变量stop上加volatile修饰,告诉JIT不要对其做优化。给stop加上volatile修饰后再次测试,循环结束并打印循环次数,说明线程三能读到stop的新值。
  2. 总结与推荐
    • 一般在项目开发中,推荐使用加volatile的方式来达到线程之间对共享变量的可见性,第一种方案了解即可,实际项目开发中使用较少。

在这里插入图片描述

  1. 禁止指令重排序特性介绍
    • 含义:用Volatile修饰的共享变量在读写时加入屏障,防止其他读写操作越过屏障,阻止重排序。
  2. 指令重排序现象分析

在这里插入图片描述

在这里插入图片描述

  1. 多线程测试工具介绍及使用

    • 工具及注解:引入jc stress test工具,在方法上加@actor注解保证方法内代码在同一线程执行,测试结果才正确;@outcome注解可打印输出结果。
      在这里插入图片描述
  2. 用Volatile关键字解决指令重排序问题

    • 解决方法:在共享变量上加Volatile关键字(value to),在y变量上加Volatile关键字后再次测试,指令重排序问题解决。
  3. 加Volatile关键字解决指令重排序及原理说明

    • 原理:在共享变量上加入不同屏障,阻止其他读写操作越过屏障。加在y上时,写操作阻止上方其他写操作往下走,读操作阻止下方其他读操作往上走,将y与其他读写操作隔离。
    • 测试验证:将Volatile关键字加到x上,经过六轮测试10的情况仍很多,说明加到x上不能禁止指令重排序,因为写操作加的屏障只能阻止上方指令往下走,不能阻止下方指令往上走,读操作同理。
  4. 尝试在不同变量上加关键字及分析

    • 变量都加关键字:两个变量都加Volatile关键字能解决问题,但会影响性能,因为指令重排序对CPU是一种优化,可减轻CPU压力。
    • 使用技巧:写变量时修饰的变量放在代码最后位置,读变量时修饰的变量放在代码最上面,这样加载Volatile较少且能解决指令重排序问题。
  5. 课程总结

    • Volatile关键字特性
      • 保证线程间可见性,阻止编译器优化,使一个线程对共享变量的修改对其他线程可见。
      • 禁止指令重排序,通过在读写共享变量时加入屏障来防止其他读写操作越过屏障,达到防止重排序的效果。

文章目录

  • synchronized
      • 1. 基本概念与作用
      • 2. 使用方式
      • 3. 底层原理(基于monitor)
      • 4. 面试题回答要点
      • 5. 注意事项与进阶理解
  • Java中synchronized锁相关知识总结
  • Java内存模型(JMM)
  • CAS概述及重要性
  • volatile关键字
  • AQS简介


在这里插入图片描述

AQS简介

  1. AQS简介与对比
    • AQS定义:AQS是抽象队列同步器(AbstractQueuedSynchronizer),是JUC中提供的一种锁机制,作为其他组件(如ReentrantLock、Semaphore、CountDownLatch等)的基础框架,由Java语言实现。
    • 对比Synchronized
      • 实现方式:Synchronized是关键字,由C++语言实现;AQS是纯粹的Java API。
      • 锁释放:Synchronized自动释放锁;AQS需要手动开启和关闭锁(因为是API)。
      • 性能特点:在锁竞争激烈时,Synchronized会自动升级为重量级锁,性能较差;AQS提供多种解决方案,性能较好。
  2. AQS基本工作机制

在这里插入图片描述

  • 状态管理:内部有一个由volatile修饰的状态state,保证多个线程之间的可见性,0表示无锁,1表示有锁。线程获取锁时需修改state值,若当前state为0,线程可将其改为1并持有锁;若state为1,修改失败的线程进入队列等待。
    - 队列结构:维护一个先进先出的双向队列,内部是双向链表,有指向队列头部(最早的线程)的head属性和指向队列最后一个元素(最新的线程)的tail属性。当持有锁的线程执行完,将state改为0(无锁状态)并唤醒队列中的头元素线程去持有锁。
  1. 多线程抢资源与原子性保证
    • 多线程抢锁情况:多个线程同时抢资源时,例如线程0和线程四同时尝试修改state(初始为0),AQS使用CAS(Compare and Swap)操作来保证原子性,最终只有一个线程(如线程零)能抢到锁,将state改为1,其他线程进入队列等待,同时tail指向最新进来的线程。
  2. 公平锁与非公平锁实现

在这里插入图片描述

  • 非公平锁:当线程0持有锁并释放后(state由1改为0),会唤醒队列中的头元素线程(如线程一)去抢锁,此时若有新线程(如线程五)也来抢锁,(新线程5) 与队列中的 (线程1) 共同竞争资源,队列中的其他线程还在继续等待,这就是非公平锁。
    - 公平锁:新线程(如线程五)到来后,不能直接抢资源,而是直接到队列的最后一个元素等待。当state为0时,直接唤醒队列中的头元素线程(如线程一)去持有锁,遵循先来先得原则,类似生活中的排队,这就是公平锁。在AQS的不同实现类中,公平锁和非公平锁都可实现。
  1. 总结
    • 回答AQS相关面试题时,应提及AQS是队列同步器和锁机制,作为基础框架被其他类使用;其内部维护先进先出双向队列存储排队线程;通过state控制锁,默认0为无锁,线程通过修改state获取锁,多个线程修改state时用CAS保证原子性。

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

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

相关文章

前端项目 npm报错解决记录

1.首先尝试解决思路 npm报错就切换yarn , yarn报错就先切换npm删除 node_modules 跟 package-lock.json文件重新下载依 2. 报错信息: Module build failed: Error: Missing binding D:\vue-element-admin\node_modules\node-sass\vendor\win32-x64-8…

深度学习中batch_size

Batch size调整和epoch/iteration的关系 训练数据集总共有1000个样本。若batch_size10,那么训练完全体样本集需要100次迭代,1次epoch。 训练样本10000条,batchsize设置为20,将所有的训练样本在同一个模型中训练5遍,则…

Redis 数据类型全解析:基础与进阶应用场景

前言:Redis 是一个高性能的内存数据库,支持多种数据类型,能够高效处理各种场景的数据存储和操作。 以下是Redis数据类型的数据结构及其应用场景的详细说明: 基础数据类型 1. 字符串 (String) 底层数据结构 Simple Dynamic Stri…

评分模型在路网通勤习惯分析中的应用——提出问题(1)

1、问题的由来、目标和意义 最近一段时间和公司其它业务部门讨论时,发现一个有趣的交通路网问题,车辆从S点行驶到V点共用时40分钟,这段时间内路网中的卡口摄像头识别到了车辆通过的信息。如下图所示: 设计师需要通过这些有限的路…

leetcode 面试经典 150 题:删除有序数组中的重复项

链接删除有序数组中的重复项题序号26题型数组解题方法双指针难度简单熟练度✅✅✅✅✅ 题目 给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保…

提升生产力工具

VSCODE插件 干货:用好这13款VSCode插件,工作效率提升10倍 - 程序员柠檬 - 博客园 Sourcetrail Sourcetrail 是一个开源且免费的源码阅读工具,以其强大的代码导航、可视化及跨平台支持特性,成为开发者理解复杂代码库的得力助手。…

什么是 Git Hooks?

在团队开发中,当成员提交代码的描述信息不符合约定提交规范的时候,需要阻止当前的提交,而要实现这个目的,我们就需要先来了解一个概念,叫做 Git hooks,即Git 在执行某个事件之前或之后进行一些其他额外的操…

Go语言方法和接收器类型详解

Go语言方法和接收器类型详解 1. 方法接收器类型 1.1 值接收器 值接收器方法不会改变接收器的状态,因为Go语言会在调用时复制接收器的值。因此,任何对接收器成员变量的修改都只会影响副本,而不会影响原始结构体实例。 type Person struct …

MS SQL Server 实战 排查多列之间的值是否重复

目录 需求 范例运行环境 数据样本设计 功能实现 上传EXCEL文件到数据库 SQL语句 小结 需求 在日常的应用中,排查列重复记录是经常遇到的一个问题,但某些需求下,需要我们排查一组列之间是否有重复值的情况。比如我们有一组题库数据&am…

抖去推碰一碰系统技术源码/open SDK转发技术开发

抖去推碰一碰系统技术源码/open SDK转发技术开发 碰一碰智能系统#碰碰卡系统#碰一碰系统#碰一碰系统技术源头开发 碰碰卡智能营销系统开发是一种集成了人工智能和NFC技术的工具,碰碰卡智能营销系统通过整合数据分析、客户关系管理、自动化营销活动、多渠道整合和个…

redis优化

在高并发、高性能、高可用系统中,Redis 的优化至关重要。以下是一些在面试中可以详细说明的 Redis 优化策略,以及具体的实践经验和技术亮点: 1. 数据模型与结构设计优化 使用合适的数据结构 :根据业务需求选择合适的 Redis 数据结…

WEB攻防-通用漏洞-文件上传-js验证-MIME验证-user.ini-语言特征

目录 定义 1.前端验证 2.MIME验证 3.htaccess文件和.user. ini 4.对内容进行了过滤,做了内容检测 5.[ ]符号过滤 6.内容检测php [] {} ; 7.()也被过滤了 8.反引号也被过滤 9.文件头检测 定义 文件上传漏洞是指攻击者上传了一个可执行文件(如木马…

探索与决策的完美结合:Actor-Critic 方法及其衍生算法

引言 在强化学习领域,如何让智能体学会做出最优决策是一个关键问题。Actor-Critic 方法提供了一种高效的解决方案,它结合了策略梯度(Actor)和值函数(Critic)的优点,使智能体能够在复杂的环境中…

未来网络技术的新征程:5G、物联网与边缘计算(10/10)

一、5G 网络:引领未来通信新潮流 (一)5G 网络的特点 高速率:5G 依托良好技术架构,提供更高的网络速度,峰值要求不低于 20Gb/s,下载速度最高达 10Gbps。相比 4G 网络,5G 的基站速度…

数据交易和联邦学习的背景下的安全属性

数据交易和联邦学习的背景下的安全属性 在数据交易和联邦学习的背景下,安全属性对于保护数据隐私、确保系统可靠性和维护交易公平性至关重要。以下将分析文章中涉及的安全属性以及分析这些属性的目的。 涉及的安全属性 双向认证:文章虽未明确提及传统意义上的双向认证机制,…

QWT 之 QwtPlotDirectPainter直接绘制

QwtPlotDirectPainter 是 Qwt 库中用于直接在 QwtPlot 的画布上绘制图形的一个类。它提供了一种高效的方法来实时更新图表,特别适合需要频繁更新的数据可视化应用,例如实时数据流的显示。 使用 QwtPlotDirectPainter 的主要优势在于它可以绕过 QwtPlot 的…

改变HTML元素的方式有哪些?如何在HTML中添加/替换或删除元素?

使用 JavaScript 的 DOM 操作 如果想要修改元素的样式,就要先获取元素之后再进行下一步操作 获取元素:可以使用等方法获取到需要操作的 HTML 元素。 document.getElementById() document.getElementsByClassName() document.getElementsByTagName() d…

SuperMap iClient3D for Cesium等高线标注

kele 前言 在三维地形分析中,等高线分析是一种非常重要的分析方法,它能直观的表达出地形的高低起伏特征,在三维系统中受到广泛应用。在SuperMap iClient3D for Cesium中,等高线分析是前端GPU分析,能够分析并渲染出等高…

从 x86 到 ARM64:CPU 架构的进化与未来

在计算机发展的历史长河中,x86、x64 和 ARM64 这三大主流 CPU 架构各自书写了辉煌的篇章。它们不仅代表了技术的进步,更承载着无数创新者的梦想与努力。 x86:从 16 位到 32 位的辉煌之路 诞生与崛起 1978 年,英特尔(…

红魔电竞PadPro平板解BL+ROOT权限-KernelSU+LSPosed框架支持

红魔Padpro设备目前官方未开放解锁BL,也阉割了很多解锁BL指令,造成大家都不能自主玩机。此规则从红魔8开始,就一直延续下来,后续的机型大概率也是一样的情况。好在依旧有开发者进行适配研究,目前红魔PadPro平板&#x…