【两万字面试系列】三年前的面试题。Service里面的线程安全问题

news2024/11/24 5:29:41

前言

三年前,大概是21年,那会刚学完java,然后去面试,被打的一塌糊涂,今天来盘一盘之前的面试,到底是怎样的问题整住了。然后发现了去年整的线程安全东西,也贴到文章后面了。那个贴的还不太准,慢慢完善!会整完然后发B站视频。
在这里插入图片描述

线程安全

其实简单的来看就是线程安全的问题。现在来看这些问题好像没有什么,就是比较正常的问题。但是当时掌握的确实太不好了,然后可能面试官看我太菜了,这些回答不上来就没有接着往后续问了。大家看到这个面试题会想到什么呢,会想怎么回答和解决呢。
也可以结合一个实际的项目区演示和测试一番。写一个数字的成员变逻辑写计算然后看结果。

在Spring Boot中,处理Service层的线程安全问题是非常重要的,因为Spring的Service默认是单例的,这意味着Spring容器中只会创建一个Service实例。如果Service中包含了状态(即成员变量),那么在并发环境下可能会出现线程安全问题。以下是一些处理Service层线程安全问题的建议:

  1. 避免在Service中定义有状态的成员变量

尽量不要在Service中定义有状态的成员变量。如果确实需要存储状态,考虑将这些状态存储在方法内部的局部变量中,因为局部变量是线程安全的,它们存储在每个线程自己的栈中。
2. 使用线程安全的类

如果Service需要维护跨方法调用的状态,应该使用线程安全的类或数据结构,如ConcurrentHashMap、AtomicInteger等。
3. 同步访问共享资源(使用锁机制)

当多个线程需要访问同一个资源时,可以通过同步机制来保证线程安全。可以使用synchronized关键字或java.util.concurrent.locks包中的锁机制。但是,过度同步可能会导致性能问题,因此需要谨慎使用。
4. 使用ThreadLocal

ThreadLocal可以为每个线程提供一个变量的副本,使每个线程都有自己独立的变量副本,从而避免线程安全问题。但是,使用ThreadLocal时需要注意及时清理,避免内存泄漏。

  1. 使用@Scope注解(不建议)

如果确实需要Service有状态,可以考虑使用Spring的@Scope注解来改变Bean的作用域。例如,使用@Scope(“prototype”)注解可以让Spring容器为每个请求创建一个新的Service实例,这样可以避免多个请求共享同一个Service实例的线程安全问题。但这种方式会增加对象的创建和销毁的开销。

线程安全

在这里插入图片描述

后面是自己写的,还不是很完善,会补充的完善,然后B站发视频

3.线程方面

3.1线程安全
3.1. spring如何处理线程并发问题

spring的对象是默认是单例的。如果在里面声明成员变量的话。然后一个请求创建一个线程,多个线程同时请求一个资源就会出现问题。(单例bean是线程安全的)

解决方式有两种

1.将spring声明成多例

2.使用threadloacal

3.在代码块中加入同步锁 让他编程同步的 就可以解决了

相当于把并行编程了串行,会影响服务器他吞吐量。

4.成员变量声明在方法里面

3.2 tomcat线程模型

tomcat也已经支持异步了;但是他的效率比netty这种肯定还是要低的。

BIO :tomcat6默认采用的。每个请求都要创建一个线程来处理,线程开销较大,不能处理高并发

3.4 Volatile

涉及到可见性 ,禁止重排序

volatile与cas 通过乐观锁的思想来共同完成相应的reenTrankllock,具体在锁那块3.4里面有

3.5 Servlet

单实例 ,多线程

3.6 ThreadLocal

以空间换时间,给每个线程分配一个空间

ThreadLocal 是Java里一种特殊的变量。每个线程都有一个 ThreadLocal 就是每个线程都拥有了自己独立的一个变量,竞争条件被彻底消除了。如果为每个线程提供一个自己独有的变量拷贝,将大大提高效率。首先,通过复用减少了代价高昂的对象的创建个数。其次,你在没有使用高代价的同步或者不变性的情况下获得了线程安全。

3.7 ThreadLocal可能引发内存泄漏问题

1、强引用(StrongReference)
最普遍的一种引用方式,如String s = “abc”,变量s就是字符串“abc”的强引用,只要强引用存在,则垃圾回收器就不会回收这个对象。
2、软引用(SoftReference)
用于描述还有用但非必须的对象,如果内存足够,不回收,如果内存不足,则回收。一般用于实现内存敏感的高速缓存,软引用可以和引用队列ReferenceQueue联合使用,如果软引用的对象被垃圾回收,JVM就会把这个软引用加入到与之关联的引用队列中。
3、弱引用(WeakReference)
弱引用和软引用大致相同,弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。当一个对象既有弱引用又有强引用的时候不会清楚弱引用。短时间内通过弱引用取对应的数据,可以取到,当执行过第二次垃圾回收时,将返回null。弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的isEnQueued方法返回对象是否被垃圾回收器标记。
ThreadLocal采用的是弱引用的key,当没有强引用来使用Thread Local的key时,key就会被回收。当我把ThreadLocal比作一个水桶,水桶上面有一个桶盖,我们在寻找桶的时候通过桶盖来寻找,但是当没有人惦记桶的时候,桶盖会自己丢失,这样我们没办法找到桶,也没法使用桶里的东西,这样就会导致内存泄漏。

 static class ThreadLocalMap {

        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
 }
	TreadLocalMap的Entry继承了WeakReference<ThreadLocal<?>>,即Entry的key是弱引用(该引用只存在弱引用的情况下,下一次GC]

ThreadLocal在保存的时候会把自己当做Key存在ThreadLocalMap中,正常情况应该是key和value都应该被外界强引用才对,但是现在key被设计成WeakReference弱引用了。

ThreadLocal的 key若引用 容易被GC回收,然后变成null。导入里面的 值没法被访问到,容易产生内存泄漏

3、解决方法

想象情况:如果一个线程被回收了,然后线程里面的成员变量也被回收,就没有内存泄漏的问题了。

实际情况:一般使用线程池,线程池是重复利用的,会存在内存泄漏的问题。ThreadLocal在进行数据读写的时候默认会进行一些清理的操作,找到并清理Entry里面key 为null 的数据

这个没有太好的解决方案,只能通过规范代码使用来解决最好是每次使用完ThreadLocal后,主动调用remove 方法去移除数据,把ThreadLocal 声明成全局变量,使他无法被GC回收

所以为了避免内存泄漏,在ThreadLocal使用结束以后,规范操作,执行下remove方法。

或者你可以从ThreadLocal,引出底层的Weak引用话题,再引出JVM结构以及OOM调优方面的话题。*

3.2 main方法线程(jvm创建线程过程)

一个程序创建一个jvm实例,一个进程,然后开辟一个线程

img

程序运行

JVM 在启动的时候会创建一个 VM Thread,这是所有线程的祖先,其他的线程都是由这个线程派生出去的。

在 Java 中,线程分为两种:普通线程守护线程

普通线程就是通常业务逻辑执行的代码,代码执行完成之后,普通线程也就退出了。而守护线程一般运行在后台,比如说响应命令行的操作,守护线程会在普通线程退出之后退出。

当程序执行完成之后,JVM 也要退出,程序执行完的标志就是非守护线程都退出了。当所有的非守护线程退出之后,守护线程也会退出。

程序在执行完成之后,会调用 System.exit() 方法,然后虚拟机退出,程序彻底结束。

exit 方法接收一个整型的参数,如果传入的值为 0,那么就表示程序是正常退出的,如果是任何非零的值,那么就表示是异常退出。

其实在代码中,非常不推荐调用这个方法,因为这个方法会造成一些意向不到的情况。

作者:Rayjun
链接:https://juejin.cn/post/6866744930376220679
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

3.3线程池

线程池的几个参数

包括阻塞队列这种

3.3.1线程池几大参数
3.3.1队列

无界队列oom

3.4锁

同步块大家都比较熟悉,通过 synchronized 关键字来实现;所有加上 synchronized 的方法和块语句,在多线程访问的时候,同一时刻只能有一个线程能够访问。

ConcurrentHashMap的锁

jdk1.8 使用到了 CAS+Volatile

synchronized+volatile(1.8版本)

CAS+Volatile

心想,确实是可以实现的呀!因为 AbstractQueuedSynchronizer(简称 AQS)内部就是通过 CAS + volatile(修饰同步标志位state) 实现的同步代码块。

volatile 保证了可见性和有序性,

cas 保证了原子性,像i++是三个操作,普通的保证不了这三个操作一起执行,然后使用cas 保证原子性。优点是可以保障原子性,但是缺点是有时候自旋时间太长,也会有问题。

AQS

AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock。 简单来说,AQS定义了一套框架,来实现同步类

AQS的核心思想是对于共享资源,维护一个双端队列来管理线程,队列中的线程依次获取资源,获取不到的线程进入队列等待,直到资源释放,队列中的线程依次获取资源。 AQS的基本框架如图所示:

AQS定义了一个实现同步类的框架,实现方法主要有tryAquiretryRelease,表示独占模式的资源获取和释放,tryAquireSharedtryReleaseShared表示共享模式的资源获取和释放。 源码分析如上文所述。

ReenTrankLock 锁
synchronized 锁对象头
Mysql的锁

表锁 行锁

CAS加volitale实现同步代码块,然后应用有 AQS ,

3.5 多线程 生产者与消费者

生产者 - 消费者模型 Producer-consumer problem 是一个非常经典的多线程并发协作的模型,在分布式系统里非常常见。

public void run() {
        synchronized (queue) {
            while (queue.size() == maxCapacity) { //一定要用 while,而不是 if,下文解释
                try {
                    System.out.println("生产者" + Thread.currentThread().getName() + "等待中... Queue 已达到最大容量,无法生产");
                    wait();
                    System.out.println("生产者" + Thread.currentThread().getName() + "退出等待");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if (queue.size() == 0) { //队列里的产品从无到有,需要通知在等待的消费者
                queue.notifyAll();
            }
            Random random = new Random();
            Integer i = random.nextInt();
            queue.offer(new Product("产品"  + i.toString()));
            System.out.println("生产者" + Thread.currentThread().getName() + "生产了产品:" + i.toString());
        }
    }

作者:小齐本齐
链接:https://juejin.cn/post/6872131047032553486
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

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

相关文章

D-ID Studio:数字身份认证的新纪元

随着科技的飞速发展&#xff0c;数字身份认证已逐渐成为我们日常生活中不可或缺的一部分。在这个背景下&#xff0c;D-ID Studio以其前沿的技术和创新的解决方案&#xff0c;正引领着数字身份认证的新纪元。 D-ID Studio是一个功能强大的在线平台&#xff0c;专注于提供全面的…

字节如何用A/B测试,解决增长问题的?

【软件测试面试突击班】2024吃透软件测试面试最全八股文攻略教程&#xff0c;一周学完让你面试通过率提高90%&#xff01;&#xff08;自动化测试&#xff09; 摘要&#xff1a;上线六年&#xff0c;字节跳动的短视频产品——抖音已成为许多人记录美好生活的平台。除了抖音&…

ZABBIX修改web界面的 “支持“,“帮助”,“Integrations“。等菜单按钮,百试百灵,删除修改Help,Support菜单

♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ ♥ **ZABBIX修改web界面的 “支持”&#xff0c;“帮助”,“Integrations”。等菜单按钮&#xff0c…

SpringBoot3-Web开发

1. Web场景 1. 自动配置 1、整合web场景 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency> 2、引入了 autoconfigure功能&#xff08;自动配置功能&#xff09…

数学建模【多元线性回归模型】

一、多元线性回归模型简介 回归分析是数据分析中最基础也是最重要的分析工具&#xff0c;绝大多数的数据分析问题&#xff0c;都可以使用回归的思想来解决。回归分析的任务就是&#xff0c;通过研究自变量X和因变量Y的相关关系&#xff0c;尝试去解释Y的形成机制&#xff0c;进…

OPC网关助力OPC UA/OPC DA协议数据采集

随着工业自动化程度的不断提高&#xff0c;OPC协议在数据采集、监控和控制系统中扮演着越来越重要的角色。其中&#xff0c;OPC UA和OPC DA是两种广泛应用的协议标准。而HiWoo Box作为一款功能强大的OPC网关解决方案&#xff0c;正是这些协议数据采集的得力助手。 一、OPC协议…

Guitar Pro 8.1中文版永久许可证激活2024最新24位注册激活码生成器

Guitar Pro是一款非常受欢迎的音乐制作软件&#xff0c;它可以帮助用户创建和编辑各种音乐曲谱。从其诞生以来就送专门为了编写吉他谱而研发迭代的。 尽管这款产品可能已经成为全球最受欢迎的吉他打谱软件&#xff0c;在编写吉他六线谱和乐队总谱中始终处于行业领先地位&#…

基于STM32F4的FFT(快速傅里叶变换)求信号幅值,频率,相位差

基于STM32F4的FFT&#xff08;快速傅里叶变换&#xff09;求信号幅值&#xff0c;频率&#xff0c;相位差 一。FFT原理介绍 快速傅里叶变换&#xff08;Fast Fourier Transform&#xff0c;FFT&#xff09;是一种用于高效计算傅里叶变换的算法。傅里叶变换是一种信号处理技术…

第三百八十一回

文章目录 1. 概念介绍2. 修改方法 015buttonStyle.png2.1 修改形状2.2 修改颜色2.3 修改位置 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何创建以图片为背景的页面"相关的内容&#xff0c;本章回中将介绍如何修改按钮的形状.闲话休提&#xff0c;让我们一起T…

相机,棱镜和光场

一、成像方法 Imaging Synthesis Capture 1.Synthesis&#xff08;图形学上&#xff09;合成&#xff1a;比如之前学过的光线追踪或者光栅化 2.Capture&#xff08;捕捉&#xff09;&#xff1a;把真实世界存在的东西捕捉成为照片 二、相机 1.小孔成像 利用小孔成像的相…

做测试还是测试开发,选职业要慎重!

【软件测试面试突击班】2024吃透软件测试面试最全八股文攻略教程&#xff0c;一周学完让你面试通过率提高90%&#xff01;&#xff08;自动化测试&#xff09; 突然发现好像挺多人想投测开和测试的&#xff0c;很多人面试的时候也会被问到这几个职位的区别&#xff0c;然后有测…

前端面试拼图-前端基础(二)

摘要&#xff1a;最近&#xff0c;看了下慕课2周刷完n道面试题&#xff0c;记录下... 1. offsetHeight scrollHeight clientHeight 区别 计算规则&#xff1a; offsetHeight offsetWidth : border padding content clientHeight clientWidth: padding content scrollHeight…

CIP通讯介绍(欧姆龙PLC)

什么是CIP CIP通信是Common Industrial Protocl(CIP)的简称&#xff0c;它是一个点到点的面向对象协议&#xff0c;能够实现工业器件&#xff08;传感器&#xff0c;执行器&#xff09;之间的连接&#xff0c;和高等级的控制器之间的连接。目前&#xff0c;有3种网络DeviceNet…

图像剪辑|Linux|ImageMagick的初步使用--素描,毛玻璃等特效

前言&#xff1a; ImageMagick在图像剪辑领域的地位基本等同于FFmpeg&#xff0c;和FFmpeg基本一样&#xff0c;在Linux下使用此工具的原因是该工具可以使用shell脚本批量剪辑&#xff0c;在Windows下就会比较麻烦一些了 那么&#xff0c;本文主要是记录一下ImageMagick的一些…

【python】python用户管理系统[简易版](源码+报告)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

VScode 单步断点调试Nodejs方法总结

目录 方法一 方法二 方法三 方法一 使用vscode开发nodejs程序,能够启动单步调试模式,在指定代码处添加断点,像chrome、firefox浏览器上一样进行JavaScript的调试。 新建一个nodejs的工程,编写代码后,配置代码调试的步骤: 1、切换到代码调试界面 2、界面提示,新建一…

rust多个mod文件引用和文件夹mod使用注意事项

如果mod文件都在同一级目录&#xff0c;则直接使用就可以&#xff0c;因为rust文件都是一个隐藏的mod&#xff0c;但是如果mod文件在另外一个目录下面&#xff0c;就需要在目录下面声明一个mod.rs文件&#xff0c;这样才能将那个目录识别为一个mod&#xff0c;可以在mod.rs里面…

数据结构学习(三)链表

链表 1. 概念 反转链表 给出3个指针&#xff0c;一个cur&#xff0c;用于遍历链表中的每个节点&#xff0c;一个prev&#xff0c;用于保存cur指向的节点的上一个节点地址&#xff0c;还有一个after&#xff0c;用于保存cur指向的节点的下一个节点地址&#xff0c;链表操作遵循…

linux 交叉编译curl(+openssl)

一、交叉编译openssl 参考博客&#xff1a;点击跳转 二、交叉编译curl 1、源码下载 地址&#xff1a;点击跳转 2、配置 CPPFLAGS"-I/home/gui/gui/openssl/build_arm/include" LDFLAGS"-L/home/gui/gui/openssl/build_arm/lib" LIBS"-ldl" \ …

webpack基础配置及使用

webpack是什么 是一个现代 JavaScript 应用程序的静态模块打包器。当webpack 处理应用程序时&#xff0c;它会递归地构建一个依赖关系图 &#xff0c;其中包含应用程序需要的每个模块&#xff0c;然后将所有这些模块打包成一个或多个 bundle 。主要有 五个核心概念&#xff1a…