Java并发编程之锁的艺术:面试与实战指南(四)

Java并发编程之锁的艺术:面试与实战指南(四)

文章目录

  • Java并发编程之锁的艺术:面试与实战指南(四)
    • 前言
    • 二十七、什么是AQS(AbstractQueuedSynchronizer)?它在Java并发包中是如何应用的?
      • AQS的主要特点:
      • AQS在Java并发包中的应用:
    • 二十八、什么是锁的顺序和锁偏序?
    • 二十九、Java中的锁策略有哪些?它们各自适用于什么场景?
    • 三十、什么是线程状态?Java中线程有哪些状态?
    • 三十一、请解释Java中的Happens-Before规则。
    • 三十二、什么是Java的内存屏障?它如何保证并发安全性?
    • 三十三、Java中的锁升级是什么?为什么需要锁升级?

🌈你好呀!我是 山顶风景独好
💝欢迎来到我的博客,很高兴能够在这里和您见面!
💝希望您在这里可以感受到一份轻松愉快的氛围!
💝不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
🚀 欢迎一起踏上探险之旅,挖掘无限可能,共同成长!

前言

本系列地址:
Java并发编程之锁的艺术:面试与实战指南(一)
Java并发编程之锁的艺术:面试与实战指南(二)
Java并发编程之锁的艺术:面试与实战指南(三)

二十七、什么是AQS(AbstractQueuedSynchronizer)?它在Java并发包中是如何应用的?

AbstractQueuedSynchronizer(AQS) 是Java并发包(java.util.concurrent.locks)中的一个核心类,用于构建依赖于先进先出(FIFO)等待队列的阻塞锁和相关的同步器,如ReentrantLock、Semaphore、CountDownLatch、FutureTask等。

AQS的主要特点:

  1. 内部状态(State):AQS使用一个volatile int类型的成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。
  2. FIFO队列:AQS使用一个内部的FIFO队列来完成等待队列的管理,当前线程获取同步状态失败时,AQS会将当前线程以及等待状态等信息构造成为一个节点(Node)并将其加入等待队列,同时会阻塞当前线程,当同步状态释放时,会把节点从队列中唤醒出来,使得线程能够再次尝试获取同步状态。
  3. 模板方法:AQS通过定义一系列模板方法,使得子类可以通过继承AQS并重写这些模板方法来管理同步状态,从而简化同步器的实现。

AQS在Java并发包中的应用:

  • 独占模式(Exclusive Mode)
    在独占模式下,只有一个线程能够获得同步状态,其他尝试获取同步状态的线程将被阻塞。如ReentrantLock就是基于AQS的独占模式实现的。
    AQS通过acquire(int arg)和release(int arg)两个模板方法分别实现独占模式下的获取和释放同步状态。
  • 共享模式(Shared Mode)
    在共享模式下,多个线程可以同时获得同步状态。如Semaphore、CountDownLatch就是基于AQS的共享模式实现的。
    AQS通过acquireShared(int arg)和releaseShared(int arg)两个模板方法分别实现共享模式下的获取和释放同步状态。
  • AQS的模板方法:
    • tryAcquire(int acquires): 尝试以独占模式获取同步状态。
    • tryRelease(int releases): 尝试以独占模式释放同步状态。
    • tryAcquireShared(int acquires): 尝试以共享模式获取同步状态。
    • tryReleaseShared(int releases): 尝试以共享模式释放同步状态。

子类需要重写上述方法中的一部分或全部,以实现自定义的同步器。

示例:
以ReentrantLock为例,它内部使用了一个继承自AQS的Sync类来实现锁的获取和释放。当线程调用lock()方法时,会调用AQS的acquire(int arg)模板方法,该方法会先调用tryAcquire(int acquires)尝试获取同步状态,如果获取失败,则通过addWaiter(Node node)将当前线程封装成节点并加入等待队列,并通过自旋或阻塞的方式等待获取同步状态。当线程调用unlock()方法时,会调用AQS的release(int arg)模板方法,该方法会调用tryRelease(int releases)尝试释放同步状态,并唤醒等待队列中的节点。

二十八、什么是锁的顺序和锁偏序?

  • 锁的顺序是指多个线程在尝试获取锁时遵循的特定顺序。如果所有线程都按照相同的顺序请求锁,那么可以避免出现循环等待的情况,从而避免死锁。也就是说,当多个线程需要访问多个共享资源时,它们应该按照一致的顺序来请求这些锁,这样可以确保不会出现循环等待,从而防止死锁的发生。

  • 锁偏序则是指由于锁的获取顺序不一致导致的非确定性行为。在并发编程中,当多个线程需要访问多个共享资源时,如果它们以不同的顺序获取这些锁,就可能导致锁偏序问题。这种不一致的锁获取顺序可能会导致程序的行为变得不确定,因为线程的执行顺序和锁的获取顺序都可能影响到程序的最终结果。为了避免锁偏序问题,开发人员应该尽量确保线程按照一致的顺序来获取锁,或者采用其他并发控制策略来确保程序的正确性和可预测性。

二十九、Java中的锁策略有哪些?它们各自适用于什么场景?

  1. 悲观锁与乐观锁:
    • 悲观锁:悲观锁预期数据冲突的可能性很大,因此在数据被处理前就会对数据进行加锁,并在整个数据处理过程中保持锁状态。这种策略适用于写操作频繁的场景,因为写操作往往伴随着数据的修改,冲突的可能性较大。Java中的synchronized关键字和ReentrantLock类就是悲观锁的实现。
    • 乐观锁:乐观锁预期数据冲突的可能性很小,因此它不会立即对数据进行加锁,而是在数据提交更新时,才会正式对数据冲突与否进行检测。如果发生冲突,则返回失败信息,让用户决定如何去做。这种策略适用于读操作频繁的场景,因为读操作不会修改数据,冲突的可能性较小。Java中的AtomicInteger、AtomicStampedReference以及CAS(Compare-and-Swap)算法等都是乐观锁的实现。
  2. 重量级锁与轻量级锁
    • 重量级锁:加锁解锁的开销比较大,通常涉及到内核态的切换。这种锁适用于竞争激烈的场景,因为当多个线程同时请求锁时,重量级锁可以通过阻塞等待的方式,确保线程安全地访问共享资源。然而,由于重量级锁的开销较大,因此可能会降低程序的性能。Java中的synchronized关键字在JDK 1.6之前主要是重量级锁的实现。
    • 轻量级锁:加锁解锁的开销比较小,通常只在用户态完成。这种锁适用于竞争不激烈的场景,因为轻量级锁可以通过自旋等待的方式,尝试获取锁,从而避免线程被阻塞和唤醒的开销。当自旋达到一定次数仍未获取到锁时,轻量级锁会升级为重量级锁。Java中的synchronized关键字在JDK 1.6及以后版本对锁的实现进行了优化,引入了偏向锁和轻量级锁等概念,使得在大多数情况下,synchronized的性能得到了显著提升。
  3. 公平锁与非公平锁:
    • 公平锁:表示线程获取锁的顺序是按照线程请求锁的时间早晚来决定的,也就是最早请求锁的线程将最先获得锁。这种策略可以保证线程的公平性,但是可能会降低程序的吞吐量,因为线程可能需要等待更长的时间才能获取到锁。Java中的ReentrantLock类可以通过设置构造函数中的fair参数为true来实现公平锁。
    • 非公平锁:与公平锁相反,非公平锁不保证线程获取锁的顺序。这种策略可能会提高程序的吞吐量,因为线程在等待较短的时间后就有可能获取到锁。然而,非公平锁可能会导致某些线程长时间得不到执行,从而影响程序的公平性。Java中的ReentrantLock类默认实现的是非公平锁。

此外,还有偏向锁、行锁、表锁、页锁等锁策略,它们各自适用于不同的场景。例如,偏向锁适用于只有一个线程访问共享资源的场景;行锁适用于需要对数据库表中的某一行进行加锁的场景;表锁适用于需要对整个数据库表进行加锁的场景;页锁则介于行锁和表锁之间,适用于需要对数据库表中的某一页进行加锁的场景。

三十、什么是线程状态?Java中线程有哪些状态?

  1. 新建状态(NEW):
    当线程对象被创建后,即进入了新建状态,此时它和其他Java对象一样,仅仅由Java虚拟机为其分配了内存,并初始化了其成员变量的值,线程对象本身是还没有生命的。和其他Java对象不同的是,线程对象是一个特殊的对象,它被创建后,系统必须为其分配资源才能执行,当调用线程的start()方法时,此线程才进入可运行状态,不同线程的启动均由Java运行系统统一调度和控制的。
  2. 就绪状态(Runnable):
    线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
  3. 运行状态(Running):
    线程获得CPU资源并执行程序代码时,线程便处于运行状态。此时,它不一定正在执行程序代码,因为Java采用的是抢占式调度模式,线程之间根据优先级自动调度。当然,也可以手动调用Thread.yield()方法来让出CPU使用权,或者调用Thread.sleep(int milltime)方法使当前线程休眠一段时间。
  4. 阻塞状态(Blocked):
    阻塞状态是指线程因为某种原因放弃了CPU使用权,也即让出了CPU time slice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得CPU time slice转到运行(running)状态。阻塞的情况分三种:
    • 等待阻塞:运行状态的线程执行wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
    • 同步阻塞:运行状态的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
    • 其他阻塞:运行状态的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
  5. 等待状态(Waiting):
    进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
  6. 超时等待状态(Timed Waiting):
    该状态不同于等待(Waiting)状态,它可以在指定的时间后自行返回。
  7. 终止状态(Terminated):
    线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

三十一、请解释Java中的Happens-Before规则。

Java中的Happens-Before规则是Java内存模型(Java Memory Model, JMM)中的一个核心概念,用于描述和保证多线程环境下操作的可见性和顺序性。它定义了一种偏序关系,即当一个操作A Happens-Before操作B时,意味着A的操作结果对B是可见的,并且A的执行顺序排在B之前。

Happens-Before规则可以确保在并发环境下,操作之间的依赖关系得到正确的执行和可见性保证。这个规则并不是按照字面意思理解的“先行发生”,而是可以理解为“先行可见”,即A操作的结果在B操作开始之前就已经对B可见。

Happens-Before规则包括以下几种情况:

  • 程序顺序规则:在一个线程内,按照程序代码顺序,前面的操作Happens-Before于后续的任意操作。这是最基本的规则,它确保了单线程内操作的顺序性。
  • 监视器锁规则:对一个监视器锁的解锁操作Happens-Before于随后对这个监视器锁的加锁操作。这个规则确保了持有同一个监视器锁的线程之间的操作顺序。
  • volatile变量规则:对一个volatile变量的写操作Happens-Before于后续对这个volatile变量的读操作。这个规则保证了volatile变量的可见性。
  • 线程启动规则:Thread对象的start()方法调用Happens-Before于该线程的每一个动作。这确保了线程启动后,主线程对该线程对象的修改对其他线程是可见的。
  • 线程终止规则:线程中的所有操作都Happens-Before于任何线程检测到该线程的终止。这确保了在线程终止前,其他线程对该线程对象的修改是可见的。
  • 线程中断规则:对线程interrupt()方法的调用Happens-Before于被中断线程的代码检测到中断事件的发生。这确保了中断操作的及时性和可见性。
  • 对象的终结规则:一个对象的初始化完成(构造函数执行结束)Happens-Before于它的finalize()方法的开始。这确保了对象在被回收前,其状态对其他线程是可见的。

三十二、什么是Java的内存屏障?它如何保证并发安全性?

内存屏障的主要作用包括:

  • 禁止重排序:内存屏障可以防止编译器和处理器对指令进行重排序,确保按照程序员编写的代码顺序执行。这是非常重要的,因为重排序可能会导致线程之间的数据不一致和不可预测的行为。
  • 保证写入的可见性:内存屏障可以确保写入一个共享变量的操作在后续的读取操作之前对其他线程可见。这保证了数据的正确性和一致性,避免了脏读和脏写等问题。

在Java中,内存屏障主要通过以下几种方式保证并发安全性:

  • volatile关键字:当使用volatile修饰变量时,Java会在该变量的写操作和读操作之间插入StoreLoad屏障。这确保了volatile变量的写操作在所有后续读操作之前完成,从而保证了volatile变量的可见性。
  • synchronized关键字:synchronized块内部也会插入内存屏障,以确保在同步块内的写操作在离开同步块时将数据更新到内存,并且后续读操作只能从内存中读取数据,而不能从缓存中读取。这保证了synchronized块内的数据对其他线程是可见的。

三十三、Java中的锁升级是什么?为什么需要锁升级?

为什么需要锁升级?

锁升级的主要目的是为了优化多线程环境下的性能,并平衡线程安全和性能之间的需求。具体来说,锁升级有以下几个方面的作用:

  • 提高性能:在锁竞争不激烈的情况下,使用低级别的锁(如偏向锁、轻量级锁)可以减少线程挂起和上下文切换的开销,从而提高系统的整体性能。当锁竞争变得激烈时,再逐渐升级到高级别的锁(如重量级锁),以保证线程安全。
  • 减少线程阻塞:偏向锁和轻量级锁都试图通过减少线程挂起和上下文切换来减少线程阻塞。偏向锁会在无竞争的情况下将锁偏向于第一个访问该锁的线程,而轻量级锁则使用自旋等待的方式尝试获取锁,而不是立即挂起线程。这些策略都可以减少线程阻塞,提高系统的响应速度。
  • 平衡线程安全和性能:在多线程环境下,线程安全和性能往往是相互矛盾的。锁升级策略可以根据实际的竞争情况动态调整锁的级别,从而在保证线程安全的前提下尽可能提高系统的性能。

锁升级的过程:

  • 无锁状态:这是锁的初始状态,表示没有线程持有该锁。
  • 偏向锁:当第一个线程访问某个同步代码块或方法时,JVM会尝试为该线程加上偏向锁。如果后续没有其他线程访问该同步块,那么持有偏向锁的线程将一直执行,无需再进行任何同步操作。
  • 轻量级锁:当有其他线程尝试访问已经被持有偏向锁的同步块时,偏向锁会升级为轻量级锁。轻量级锁会尝试使用自旋等待的方式获取锁,而不是立即挂起线程。如果自旋等待成功,则线程获取到锁并执行同步代码块;如果自旋等待失败,则锁会升级为重量级锁。
  • 重量级锁:当轻量级锁自旋等待失败或竞争情况非常激烈时,锁会升级为重量级锁。重量级锁会挂起正在等待的线程,并将线程放入等待队列中。当持有锁的线程释放锁时,会从等待队列中唤醒一个线程并赋予其锁的持有权。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

企业活动想联系媒体报道宣传如何联系媒体?

在企业的宣传推广工作中,我曾经历过一段费事费力、效率极低的时期。那时,每当公司有重要活动或新项目需要媒体报道时,我便要一家家地联系媒体,发送邮件、打电话,甚至亲自登门拜访,只为求得一篇报道。然而,这样的过程充满了不确定性和挑战,时常让我感到焦虑和压力山大。 记得有一…

vue3专栏项目 -- 项目介绍以及准备工作

这是vue3TS的项目,是一个类似知乎的网站,可以展示专栏和文章的详情,可以登录、注册用户,可以创建、删除、修改文章,可以上传图片等等。 这个项目全部采用Composition API 编写,并且使用了TypeScript&#…

亚马逊产品排名提升全攻略:自养号测评干货

之前我们一同探讨了亚马逊产品排名的多种类型,现在让我们回到正题,探讨一下如何才能有效地提升产品排名,从而吸引并抓住平台的流量,最终将其转化为可观的销量。 首先,卖家必须明晰亚马逊的排名机制,它主要基…

网页版Figma汉化

最近学习Figma,简单介绍一下网页版Figma的汉化方法 1.打开网址:Figma软件汉化-Figma中文版下载-Figma中文社区 2.下载汉化插件离线包 解压汉化包 3.点开谷歌的管理扩展程序 4.点击加载已解压的扩展程序,选择刚刚解压的包 这样就安装好了汉化…

从0到1开发一个vue3+ts项目(一)

1. 环境配置 1.1 安装node 使用官方安装程序 前往 Node.js 官网:访问 Node.js 官网,下载适合你操作系统的安装程序。运行安装程序:下载完成后,双击安装程序并按照提示进行安装。验证安装:安装完成后,在终…

顺序表经典算法OJ题-- 力扣27,88

题1: 移除元素 题2: 合并两个有序数组 一:题目链接:. - 力扣(LetCode) 思路:(双指针法) 创建两个变量src,dst 1)若src指向的值为val&#xf…

Qt复习第二天

1、菜单栏工具栏状态栏 #include "mainwindow.h" #include "ui_mainwindow.h" #pragma execution_character_set("utf-8"); MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow) {ui->setupUi(this);//菜…

粤嵌—2024/4/26—跳跃游戏 ||

代码实现&#xff1a; 方法一&#xff1a;回溯 历史答案剪枝优化——超时 int *dis;void dfs(int k, int startindex, int *nums, int numsSize) {if (dis[startindex] < k) {return;}dis[startindex] k;for (int i 0; i < nums[startindex]; i) {if (startindex i &…

嫁接打印的技术要点

所谓嫁接打印&#xff0c;是一种增减材混合制造的方式。它将已成形的模具零件当作基座&#xff0c;在此基础上“生长”出打印的零件。其中基座通常采用传统加工方式制造&#xff0c;而打印部分则使用专用的金属粉末&#xff0c;通过 3D 打印技术成型。 嫁接打印之所以备受欢迎&…

4.nginx.pid打开失败以及失效的解决方案

一. nginx.pid打开失败以及失效的解决方案 1.错误图片&#xff1a; 2.解决方法 步骤1&#xff1a;进入这个目录 /var/run/nginx,提示没有文件或目录&#xff0c;则使用mkdir创建这个目录。 步骤2&#xff1a;然后 ./nginx -s reload 运行,是一个无效的PID 步骤3&#xff1a;使…

SMI接口

目录 SMI 接口帧格式读时序写时序 IP 设计IP 例化界面IP 接口IP 验证 SMI 接口 SMI&#xff08;Serial Management Interface&#xff09;串行管理接口&#xff0c;也被称作 MII 管理接口&#xff08;MII Management Interface&#xff09;&#xff0c;包括 MDC 和 MDIO 两条信…

【字符串】Leetcode 二进制求和

题目讲解 67. 二进制求和 算法讲解 为了方便计算&#xff0c;我们将两个字符串的长度弄成一样的&#xff0c;在短的字符串前面添加字符0&#xff1b;我们从后往前计算&#xff0c;当遇到当前计算出来的字符是> 2’的&#xff0c;那么就需要往前面进位和求余 注意&#xf…

《QT实用小工具·六十二》基于QT实现贝塞尔曲线画炫酷的波浪动画

1、概述 源码放在文章末尾 该项目实现了通过贝塞尔曲线画波浪动画&#xff0c;可控制 颜色密度速度加速度 安装与运行环境 语言&#xff1a;C 框架&#xff1a;Qt 11.3 平台&#xff1a;Windows 将屏幕水平平均分为10块&#xff0c;在一定范围内随机高度的12个点&#xff08;…

OAuth 2.0 和 OAuth 2.1

OAuth 2.0 和 OAuth 2.1比较&#xff1a; OAuth 2.0 和 OAuth 2.1 是授权框架的不同版本&#xff0c;它们用于允许应用程序安全地访问用户在另一个服务上的数据。以下是它们之间的一些主要区别&#xff1a; 安全性增强&#xff1a;OAuth 2.1 旨在提高安全性&#xff0c;它整合…

C语言/数据结构——每日一题(移除链表元素)

一.前言 今天在leetcode刷到了一道关于单链表的题。想着和大家分享一下。废话不多说&#xff0c;让我们开始今天的知识分享吧。 二.正文 1.1题目要求 1.2思路剖析 我们可以创建一个新的单链表&#xff0c;然后通过对原单链表的遍历&#xff0c;将数据不等于val的节点移到新…

MySQL索引(聚簇索引、非聚簇索引)

了解MySQL索引详细&#xff0c;本文只做整理归纳&#xff1a;https://blog.csdn.net/wangfeijiu/article/details/113409719 概念 索引是对数据库表中一列或多列的值进行排序的一种结构&#xff0c;使用索引可快速访问数据库表中的特定信息。 索引分类 主键索引&#xff1a…

微信群发用什么软件最安全?微信群发软件哪个好?微信群发助手软件在哪里?

今天给大家推荐一款我们目前在使用的电脑群发工具掘金小蜜&#xff0c;不仅可以无限多开&#xff0c;方便你同时管理多个账号&#xff0c;群发功能更是十分强大&#xff0c;轻松释放你的双手。 掘金小蜜&#xff08;只支持Win7及以上操作系统&#xff0c;没有推Mac版和手机客户…

【算法入门赛】B. 自助店评分(C++、STL、推荐学习)题解与代码

比赛地址&#xff1a;https://www.starrycoding.com/contest/8 题目描述 在上一场的入门教育赛中&#xff0c;牢 e e e找到了所有自助店的位置&#xff0c;但是他想发现一些“高分好店”&#xff0c;于是他利用爬虫技术从“小众点评APP”中爬取了武汉所有自助店的评分。 评分…

[笔试训练](十八)

目录 052:字符串压缩 053:chika和蜜柑 054:01背包 052:字符串压缩 压缩字符串(一)_牛客题霸_牛客网 (nowcoder.com) 题目&#xff1a; 题解&#xff1a; 双指针模拟 class Solution { public:string compressString(string param) {int nparam.size();string ret;int num…

【线性代数】英语版听课笔记

线性代数 - 北京航天航空大学&#xff08;英文版&#xff09;_哔哩哔哩_bilibili 39.concept of vector space in this lecture we will studyvector space&#xff0c; the concept of basis dimension and coordinates 向量空间的维数&#xff1a;向量空间的基底所含向量的…