Java锁常见面试题

图片引用自:不可不说的Java“锁”事 - 美团技术团队

1 java内存模型

java内存模型(JMM)是线程间通信的控制机制。JMM定义了主内存和线程之间抽象关系。线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存,本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

2 锁的概念

2.1 什么是锁

在Java中的锁主要是用于保障线程在多并发的情况下数据的一致性。就是实现并发的原子性。

2.2 为什么加锁

在多线程编程中为了保证数据的一致性,我们通常需要在使用对象或者调用方法之前加锁,这时如果有其他线程也需要使用该对象或者调用该方法,则首先要获得锁,如果某个线程发现锁正在被其他线程使用,就会进入阻塞队列等待锁的释放,直到其他线程执行完成并释放锁,该线程才有机会再次获取锁并执行操作。这样做可以保障了在同一时刻只有一个线程持有该对象的锁并修改该对象,从而保障数据的安全性。

3 悲观锁 VS 乐观锁

悲观锁:悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改,阻塞直到拿到锁。

实现方式:Java中悲观锁是通过synchronized关键字或ReentrantLock接口来实现的。

应用场景:悲观锁适合写操作多的场景,先加锁可以保证写操作时数据正确。

乐观锁:认为在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新,则根据不同的实现方式执行不同的操作(例如报错或者自动重试)。

实现方式:乐观锁在Java中是通过使用无锁编程来实现,最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。相对于对于 synchronized 这种阻塞算法,CAS是非阻塞算法的一种常见实现。所以J.U.C在性能上有了很大的提升。当然,如果并发非常严重,那么会导致CAS的自旋非常严重,此时性能反倒不如直接使用悲观锁。

应用场景:乐观锁适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升,这样可以提高吞吐量。

// ------------------------- 悲观锁的调用方式 -------------------------
// synchronized
public synchronized void testMethod() {// 操作同步资源
}
// ReentrantLock
private ReentrantLock lock = new ReentrantLock(); // 需要保证多个线程使用的是同一个锁
public void modifyPublicResources() {lock.lock();// 操作同步资源lock.unlock();
}// ------------------------- 乐观锁的调用方式 -------------------------
private AtomicInteger atomicInteger = new AtomicInteger();  // 需要保证多个线程使用的是同一个AtomicInteger
atomicInteger.incrementAndGet(); //执行自增1

3.1 synchronized VS Lock


1、synchronized是Java关键字,在JVM层面实现加锁和解锁;Lock是一个接口,在代码层面实现加锁和解锁。

2、synchronized可以用在代码块上、方法上;Lock只能写在代码里。

3、synchronized在代码执行完或出现异常时自动释放锁;Lock不会自动释放锁,需要在finally中显示释放锁。

4、synchronized会导致线程拿不到锁一直等待;Lock可以设置获取锁失败的超时时间。

5、synchronized无法得知是否获取锁成功;Lock则可以通过tryLock得知加锁是否成功。

6、synchronized锁可重入、不可中断、非公平;Lock锁可重入、可中断、可公平/不公平,并可以细分读写锁以提高效率

7、都是悲观锁,都可以解决线程安全问题

3.2 CAS

CAS全称 Compare And Swap(比较与交换),是一种无锁算法。在不使用锁(没有线程被阻塞)的情况下实现多线程之间的变量同步。java.util.concurrent包中的原子类就是通过CAS来实现了乐观锁。

CAS算法涉及到三个操作数:

  • 需要读写的内存值 V。
  • 进行比较的值 A。
  • 要写入的新值 B。

4 volatile

参考另外一篇文章:java并发编程中的四个关键字:ThreadLocal、Volatile、Synchronized和Atomic-CSDN博客

5 线程安全

5.1 定义

当多个线程同时访问共享资源时,可能出现数据竞争、死锁等问题,导致程序运行出错或异常

5.2 常见问题

(1)数据竞争:当多个线程同时访问同一个资源时,会导致数据不一致,比如在多线程环境下,多条线程同时修改同一个变量,可能导致该变量值不可测。

(2)死锁:多线程间同时等待对方释放资源,导致程序无法继续运行,进入死锁状态。

(3)非原子操作:某些操作需要多条指令才能完成,如果多条线程同时执行这些操作,就会出现部分执行的情况,导致程序结果不正确。

(4)内存泄漏:由于程序设计不当,可能出现内存无法回收的情况,导致内存泄漏。

多个线程可以共享一个进程的变量时,如果线程需要对这个变量进行修改操作,则可能会因为数据更新不及时导致变量信息不准确而引发线程不安全。如果线程对这个变量只有读操作,没有更新操作则这个线程没有线程安全问题。

5.3 原因

多个线程同时访问同一个共享资源且存在修改该资源

5.4 解决方法--线程同步

为了解决线程安全问题就要引出线程同步这个概念。

如何保证线程安全:队列和锁 保证线程同步的安全性,让多个线程实现先后依次访问共享资源,这样就解决了安全问题。

加锁,把共享资源进行上锁,每次只能一个线程进入访问完毕以后解锁,然后其他线程才能进来。

1、同步代码块:Synchronized

2、同步方法:Synchronized

3、Lock锁:ReentrantLock类实现了Lock。比较常用的是ReentrantLock,可以显式加锁,释放锁

6 死锁

5.1 什么是死锁

不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。

出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。

如果没有外部干预,线程会一直阻塞无法往下执行,这些一直处于相互等待资源的线程就称为死锁线程。

6.2 产生死锁的四个必要条件


1.资源互斥:对所分配的资源进行排它性控制,锁在同一时刻只能被一个线程使用。

2.不可剥夺:线程已获得的资源在未使用完之前,不能被剥夺,只能等待占有者自行释放锁。

3.请求等待:当线程因请求资源而阻塞时,对已获得的资源保持不放

4.循环等待:线程之间的相互等待

6.3 避免死锁

按照死锁发生的四个条件,只需要破坏其中的任何一个,就可以解决,但是,互斥条件是没办法破坏的,因为这是互斥锁的基本约束,其他三方条件都有办法来破坏:

1、设置超时时间,超时可以退出,防止死锁

2、降低锁的使用粒度,尽量不要几个功能用同一把锁

3、可以一次性申请所有的资源,这样就不存在等待了

7 分布式锁

7.1 为什么

假设我们把代码部署到多台服务器上,还能生效吗?答案是否定的,这时分布式锁应运而生。

public synchronized void test1() {System.out.println("获取到锁1");
}
public void test2() {synchronized (Test.class) {System.out.println("获取到锁2");}
}

7.2 Redis分布式锁

图片来源于:Java分布式锁面试题_殷十娘的博客-CSDN博客

8 sleep() VS wait()


1、sleep方法没有释放锁,而wait方法释放了锁。

2、都可以暂停线程的执行。

3、wait()通常被用于线程交互/通信,sleep()通常被用于暂停执行。

4、wait()方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的notify()或者notifyAll方法,sleep方法执行完成后,线程会自动苏醒。或者可以使用wait(long timeout)超时后线程会自动苏醒。

9 单例模式

加入violate可以避免重排序

class SingletonClass {private static violate SingletonClass instance = null;public static SingletonClass getInstance() {if (instance == null) {synchronized(SingletonClass.class) {if (instance == null) {instance = new SingletonClass();}}}return instance;}
}

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

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

相关文章

AI:59-基于深度学习的行人重识别

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

SOEM源码解析——ecx_init_context(初始化句柄)

0 工具准备 1.SOEM-master-1.4.0源码1 ecx_init_context函数总览 /*** brief 初始化句柄* param context 句柄*/ void ecx_init_context(ecx_contextt *context) {int lp;*(context->slavecount) 0;/* clean ec_slave array *//* 清空从站信息数组 */memset(context->…

win10设置windows永不更新

以下方法能全部设置都要全部设置。 方法一:Windows设置 要想关闭Win10自动更新,比较简单的一种方法就是进入到Windows设置中,将Windows更新直接关闭。步骤如下: 1、按“Windows I”键,打开Windows设置,再…

服务器数据恢复—Zfs文件系统下文件被误删除的如何恢复数据?

服务器故障: 一台zfs文件系统服务器,管理员误操作删除服务器上的数据。 服务器数据恢复过程: 1、将故障服务器所有磁盘编号后取出,硬件工程师检测所有硬盘后没有发现有磁盘存在硬件故障。以只读方式将全部磁盘做扇区级别的镜像备…

gcc -static 在centos stream8 和centos stream9中运行报错的解决办法

gcc -static 在centos stream8 和centos stream9中运行报错的解决办法: 报/usr/bin/ld: cannot find -lc 我们下载glibc-static: 选择x86_64的。 还有一个是libxcrypt-static,依旧在这个网站里搜。 rpm -ivh glibc-static-2.28-239.el8.x…

使用seldom编写http接口用例

在编写接口用例的过程中,针对一个接口,往往只是参数不同,那么参数化就非常有必要了。 seldom 中参数化的用法非常灵活,这里仅介绍file_data() 的N种玩法。 二维列表 当参数比较简单时可以试试下面的方式。 参数化数据 {"…

onnx 模型加载部署运行方式

1.通过文件路径的onnx模型加载方式: 在onnxruntime下面的主要函数:session Ort::Session(env, w_modelPath.c_str(), sessionOptions); 这里的文件路径是宽字节的,通过onnx文件路径直接加载模型。 在opencv下使用dnn加载onnx模型的主要函数: std::string model…

北京联通iptv组播配置

多年前折腾过iptv,近期搬家换了个大电视,打算把iptv配置好了,尽管不怎么看,但聊胜于无。 其实很简单,用到了一些工具,记录如下 1. openwrt配置 因为有软路由,所以就借助openwrt了,一…

【qemu逃逸】HITB2017-babyqemu 2019数字经济-qemu

前言 由于本地环境问题,babyqemu 环境都没有起起,这里仅仅做记录,exp 可能不正确。 HITB2017-babyqemu 设备逆向 设备定位啥的就不说了,先看下实例结构体: 其中 dma_state 结构体如下: 这里看字段猜测…

linux入门到地狱

linux—001入门 IT圈必备(前端工作者用的比较少) 老旧电脑跑linux不容易卡 我代码没保存windows闪退,僵停(vs2019卡掉线),重启更新,占用cpu内存服务报错pip各种bug 出来生态环境友好其他的全是bug(bug时间成本超过了windows快捷友好生态) 那就说明wind…

ICP学习记录

1. 流程图 ICP(一)原理详解_icp原理-CSDN博客 ICP算法详解——我见过最清晰的解释-CSDN博客 ICP算法理解-CSDN博客 ICP知识点梳理笔记_icp非凸_KalutSirocco的博客-CSDN博客 【精选】【图像配准】点云配准ICP算法介绍:基础流程、ICP算法…

MyBatis-Plus复习总结(一)

文章目录 一、环境搭键二、基本CRUD2.1 BaseMapper2.2 插入2.3 删除2.4 修改2.5 查询 三、通用Service四、常用注解4.1 雪花算法4.2 注解TableLogic 五、条件构造器和常用接口5.1 Wrapper介绍5.2 QueryWrapper5.3 UpdateWrapper5.4 condition5.5 LambdaQueryWrapper5.6 LambdaU…

Gradle中的依赖Dependencies说明与使用总结

【1】依赖的方式 Gradle 中的依赖分别为直接依赖,项目依赖,本地jar 依赖。 dependencies {//①.依赖当前项目下的某个模块[子工程]implementation project(:subject01)//②.直接依赖本地的某个jar文件implementation files(libs/foo.jar, libs/bar.jar…

行业安卓主板-基于RK3568/3288/3588的电子班牌/人脸识别门禁/室内对讲门禁方案解决方案(二)

电子班牌 智能电子班牌可在主页实时显示班级全面的基本信息,包括天气、班名、课程表、值日表等,并发布学校通知、班级通知。学生可刷卡自动登陆系统进行课堂反馈,教师和家长可及时了解教学反馈,打通学校、教师、学生之间的互动通…

Hive 解析 JSON 字符串数据的实现方式

文章目录 通过方法解析现实示例 通过序列化实现示例 通过方法解析现实 在 Hive 中提供了直接解析 JSON 字符串数据的方法 get_json_object(json_txt, path),该方法参数解析如下: json_txt:顾名思义,就是 JSON 字符串;…

elasticsearch索引按日期拆分

1.索引拆分原因 如果单个索引数据量过大会导致搜索变慢,而且不方便清理历史数据。 例如日志数据每天量很大,而且需要定期清理以往日志数据。例如原索引为sc_all_system_log,现按天拆分索引sc_all_system_log20220902,sc_all_syste…

B-DS二叉树_输出所有目标路径

Description 给定二叉树和一个整数目标targetSum,输出所有从根结点到叶子结点的路径总和等于targetSun的路径。 Input 第一行输入t,表示有t个测试样例。 第二行起,每一行首先输入一个整数targetSum,接着输入n,接着输…

AMD老电脑超频及性能提升方案及实施

收拾电子元件的时候找到了若干古董的CPU 其中有一个X3 440 是原来同学主板烧了之后给我的,我从网上配了AM2 昂达主板,然后又买了AMD兼容内存,组成了win7 64位电脑,用起来非常不错,我把硬件配置和升级过程说明下&#x…

ELK极简上手

目录 引言 首先,下载相关的包 其次,安装启动elasticsearch 下一步,安装并启动logstash 最后,安装并启动kibana 进一步的,测试数据的流动 引言 最近整理电脑发现之前的一篇ELK极简入门笔记,现整理发出…

开发小程序需要多少钱?

随着移动互联网的快速发展,小程序已经成为了企业、个人创业者获取用户、提升品牌影响力的重要工具。然而,对于许多初次接触小程序的人来说,开发小程序需要多少钱,是他们最关心的问题。 首先我们需要明确的是,开发小程…