【JAVA】JAVA后端,JVM,并发之可见性与原子性,并发与锁


1.Java后端

1.1 编程基础

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.2 应用框架

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.3 工具运维

在这里插入图片描述
在这里插入图片描述

2.JVM

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.1 程序计数器

在这里插入图片描述
在这里插入图片描述

2.2 栈和栈帧

当调用第一个方法时,给第一个方法划分一段栈帧空间并压入栈内。当这个方法执行完了将这个方法对应的栈帧出栈也就是释放这个方法所占用的内存。一个栈内有没有可能多个栈帧存在?有的,调用方法1,方法1间接调用方法2,为方法2调用一块内存入栈,方法2又调用了方法3(尾递归)。。。方法3调用结束就把栈帧3内存释放掉。。。。
在这里插入图片描述
在这里插入图片描述
如下main调用method1,method1调用method2,活动栈帧就是栈顶部的正在执行的方法
在这里插入图片描述
栈内存就是一次次方法调用产生的栈帧内存,栈帧内存在每次方法结束后被弹出栈自动回收掉,不需要垃圾回收管理栈内存,垃圾回收只回收堆内存中无用对象。栈内存大小可通过运行代码时虚拟机参数指定,栈内存划的越大反而让线程数变少:因为物理内存大小一定的,比如一个线程使用了1M栈内存,总共物理内存有500M,理论上有500个同时运行。如果对每个线程的栈内存设置了2M,只能同时运行250个线程。所以栈内存不是分配越大越好,划分大了只能更多次进行方法递归调用,不会增强运行效率反而影响到线程数目变少,一般采用系统默认栈内存大小就可以
在这里插入图片描述
如下解释如上第三个问题
在这里插入图片描述
在这里插入图片描述
x变量是m1方法内的局部变量,我们说一个线程对应一个栈,线程内每次方法调用会产生一个新的栈帧,如下线程不会产生安全问题
在这里插入图片描述
如下不加线程保护的话会产生安全问题
在这里插入图片描述
如下只有第一个线程安全
在这里插入图片描述
栈帧过多:方法的递归调用没有设置正确的计数条件导致递归爆栈。栈帧过大(很少出现):栈帧里都是些局部变量,方法参数,占用的内存都很小(一个int变量4字节,默认栈大小1M左右)
在这里插入图片描述
在这里插入图片描述
如下设置Xss栈大小256k,比默认小。如上代码执行减少到5000多次
在这里插入图片描述
两个类循环引用导致将java对象解析为json字符串数据StackOverflowError
在这里插入图片描述

2.3 线程诊断

在这里插入图片描述
top命令只能定位到进程,无法定位到线程
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.4 堆和方法区

本地方法:不是由java编写的方法。java不能与操系底层打交道,需要C/C++本地方法与os底层api打交道。java可间接通过本地方法调用底层功能。本地方法栈就是给本地方法提供的内存空间。程序计数器,虚拟机栈(只要局部变量不在逃逸出方法的作用范围就是线程安全的),本地方法栈都是线程私有
堆和方法区是线程共享:堆中不再被引用的对象(这对象没人再使用了,但是如果不断产生对象有人用就不能作为垃圾)当成垃圾进行回收释放空闲的内存,不至于让内存被创建的对象撑爆
在这里插入图片描述
在这里插入图片描述
方法区:存储了类结构相关信息
在这里插入图片描述

3.并发之可见性与原子性

3.1 并发(线程)之可见性

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如上线程2不能立即读到线程1写后的最新变量值,多线程不可见性
在这里插入图片描述
如何解决多线程不可见性:加volatile关键字使a在主存和localcache间强制刷新一致
在这里插入图片描述

3.2 原子性(读写原子)

如果线程1基于读的变量再对变量进行写,最典型操作i++,T1和T2都进行i++操作
在这里插入图片描述
一开始i=0,经过两个线程两次i++操作结果变成了1,这显然是不对的,并且这种情况下不能用volatile保证这样操作的正确性。。T1赋给i即使刷新主存中i=1,T2已经读过0了。两个线程既有读操作,又有基于读操作的写操作,可见性只保证一个线程写另一个线程读是正确的,这里可见性不适用。现在想做的是将读操作和写操作合为一步,要么同时发生要么同时不发生(原子性)。在保证原子性同时一定以保证可见性为前提的不是并列关系(AtomicInteger类里本质上value值就是volatile的)。本身不可见的话没办法保证原子性
在这里插入图片描述
volatile不具有传染性(用volatile修饰的对象的内部属性不具有可见性,反之用volatile修饰的内部属性也不能保证所在对象的可见性),volatile还有防止指令重排作用
在这里插入图片描述
用synchronized同步关键字来保证原子性发生,同步关键字同一时间只有一个线程进入代码段
在这里插入图片描述
volatile可见性关键字最轻量级(保证一个线程写,一个线程读能读到最新的值),AtomicInteger(保证既有读操作又有写操作如i++这种场景下能保证操作的原子性)基于volatile,synchronized最重量级(能保证整个代码块中所有操作都是原子性的)。多线程情况下需要自增请使用Atomicxxx类来实现

4.并发与锁

关于内存见文章:https://blog.csdn.net/weixin_43435675/article/details/105901050
在这里插入图片描述
内存,cpu(由控制器和运算器组成,通过总线与其他设备连接),io是编程中三个最重要的点。南桥连接带宽要求低的设备如是一些鼠标键盘硬盘usb设备等。北桥负责带宽比较高的设备如pcie显卡,pcie硬盘,内存这些都需要高速访问的
在这里插入图片描述
在这里插入图片描述
如下是cpu状态查看
在这里插入图片描述
如何利用cpu资源?外部资源利用都是通过操作系统提供的接口,os给了我们两种抽象即进程和线程。进程是系统资源分配,调度和管理的最小单位,比如去任务管理器查看使用内存时是看的哪个进程或哪个程序使用了多少内存而不是哪个线程,如果是哪个线程根本不知道是哪个程序里的线程没法管理。一个进程的内存空间是一套完整的虚拟内存地址空间,这个进程中所有线程都共享这一套地址空间。线程是cpu运算的最小执行单位,也就是说真正去让cpu执行任务时,cpu看到的就是线程了,cpu并不在意是哪个进程,cpu就是轮换着线程来运行并不需要知道这个线程是属于哪个进程的。cpu和并发,线程才是主角
在这里插入图片描述
nodejs用户看是单线程能应对高并发,因为io异步,回调函数形式接收io执行结果。但内核看是多线程,如下线程数12
在这里插入图片描述
如下是线程的5种状态,只有运行中是占用cpu资源的
在这里插入图片描述
线程在执行时有一定性能损耗,这些损耗来自线程的创建销毁和切换,线程本质向cpu申请计算资源,用户态转内核态。os给我们提供的去获取cpu资源形式就是线程,多线程面临多线程切换问题不得避免
在这里插入图片描述
协程是用户自定义线程但与os的线程不同,协程不进入内核态
在这里插入图片描述
synchronized是java中关键字
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章