【八股】更新
Java泛型:
原理是类型擦除,是一种在编译时期的“语法糖”。主要是两种,在编译时擦除为有边界时上界,没边界时object类。(在编译后的字节码中,所有泛型的类型信息都会被移除(擦除),并替换为它们的原始类型或边界类型,同时在必要的地方插入强制类型转换。)方法签名中也会类型擦除,有时还会方法冲突(当方法中泛型参数擦除时)。
好处:解决代码复用(集合类)。兼容低版本的代码。
对于? extends T 和 ? super T,前者是规定了泛型的上界,后者是下界。应用场景分别是比如一个List,泛型是如上类型。前者的实际类型可能是T以及其子类,所以可以读取T类型的数据;后者实际类型可以是T以及其父类,所以可以填写T类型的数据。(即PECS原则: Producer-Extends, Consumer-Super)
线程池:
线程池的参数有:核心线程数,最大线程数,线程工厂,任务队列,拒绝策略,。工作原理:当任务来到->核心线程->任务队列->最大线程->触发拒绝策略。
三次四次
https://blog.csdn.net/m0_56649557/article/details/119492899?fromshare=blogdetail&sharetype=blogdetail&sharerId=119492899&sharerefer=PC&sharesource=m0_72696598&sharefrom=from_link
专业详细。
UPD,TCP:
两个都是传输层协议。tcp是可靠的,面向连接的的控制。。协议。它能进行流量控制(滑动窗口动态调整),。需要建立连接,(三次握手,四次挥手)。
-
拥塞控制:TCP采用各种拥塞控制算法(如慢启动、拥塞避免、快速重传和快速恢复)来管理网络拥塞。
-
可靠性:Tcp利用序列号和确认号,保证数据。
区别:
连接性
-
TCP:TCP是面向连接的协议。在数据传输之前,TCP需要通过三次握手建立一个连接。三次握手过程确保了通信双方已经准备好接收和发送数据。
-
UDP:UDP是无连接的协议。它不需要在传输数据之前建立连接。发送方可以在任何时候发送数据,而不需要事先通知接收方。
可靠性
-
TCP:TCP确保数据的可靠传输。每个数据包都有一个序列号,接收方会发送确认包(ACK)确认收到数据。如果发送方没有在规定时间内收到确认包,它会重新发送数据包。
-
UDP:UDP不提供数据包的可靠性保障。它不会跟踪数据包的传输状态,也不会重传丢失的数据。这使得UDP的传输效率较高,但也更不可靠。
流量控制与拥塞控制
-
TCP:TCP实现了流量控制和拥塞控制。流量控制通过调整发送方的传输速率,确保接收方不会被淹没。拥塞控制通过检测网络拥塞,动态调整传输速率,避免网络过载。
-
UDP:UDP不具备流量控制和拥塞控制机制。发送方以固定速率发送数据包,无法适应网络条件的变化。
数据传输顺序
-
TCP:TCP保证数据包按顺序到达。它通过序列号和确认机制确保接收方按正确顺序接收到数据包。
-
UDP:UDP不保证数据包按顺序到达。每个数据包独立传输,接收方收到的数据包顺序可能与发送顺序不同。
数据库数据隔离机制:
事务的四个特性:
a 原子性:同时成功与失败。比如转账这个操作要么成功要么失败。靠事务回滚控制。
c一致性:数据前后需正确且完整。比如说转账前后总和还是500块。靠数据库约束和业务逻辑设计控制。
i隔离性:数据之间的修改应该是隔离的。比如同时转账对于数据修改不应该冲突。靠锁机制和多版本控制mvcc控制。
d持久性:数据库数据的修改应该是持久的。事务成功后数据不会丢失。靠数据写入磁盘控制。
隔离机制:RU RC RR serializable
可重复读与锁关系:
3. 为什么说 REPEATABLE READ 有行锁?
写操作自动加锁:所有 UPDATE/DELETE 操作都会自动获取排他行锁
显式加锁:通过 SELECT...FOR UPDATE 可以显式获取行锁
防止幻读:通过间隙锁机制,实际上比简单的行锁更严格
4. 与 READ COMMITTED 的区别
在 READ COMMITTED 隔离级别下:
只有简单的行锁,没有间隙锁
写操作完成后会立即释放锁
更容易出现幻读问题
而在 REPEATABLE READ 下:
使用更严格的临键锁
锁会保持到事务结束
通过间隙锁防止幻读
原文链接:https://blog.csdn.net/qq_33764173/article/details/149199395?fromshare=blogdetail&sharetype=blogdetail&sharerId=149199395&sharerefer=PC&sharesource=m0_72696598&sharefrom=from_link
Redis的RDB和AOF
https://blog.csdn.net/dongzh029/article/details/116499305?fromshare=blogdetail&sharetype=blogdetail&sharerId=116499305&sharerefer=PC&sharesource=m0_72696598&sharefrom=from_link
rdb: 内存快照,将redis某一时刻的数据持久化到磁盘中。在redis运行时,会单独fork出来一个子进程持久化,将数据复制到一个临时进程中,直到复制完毕,才会完全替代原复制数据文件。在数据规模较大,且对数据敏感度不高的情况下,rdb比aof更高效。
aof:是一个只追加不能改写的文件。将redis运行时的所有指令都记录下来,能够进行指令重写,比如将多条添加指令压缩成一条。如果误操作flushAll也能恢复数据。但是同等数据规模下,aof的文件体积比rdb大得多,且恢复的速度更慢。
redis相关数据结构,为什么每种数据类型一般都有两种数据结构?
Redis有五种基本数据类型:String、List、Hash、Set、ZSet。每种数据类型实际上在Redis内部都有多种实现方式(即两种或以上的底层数据结构),这样设计是为了在不同的使用场景下,选择最合适的底层数据结构,以平衡时间和空间效率。比如Hash类型,在字段数量少且值不大时使用ziplist——一种内存紧凑的连续存储结构,虽然查询是O(n),但数据量小的时候影响不大,却能显著节省内存。当字段数量或值大小超过阈值时,Redis会自动切换到hashtable,提供O(1)的查询性能。
Redis学习笔记(1)- 数据类型_redis相关数据结构,为什么每种数据类型一般都有两种数据结构-CSDN博客
volatile的作用
确保操作的可见性与有序性,但不能保证原子性。
https://blog.csdn.net/u012723673/article/details/80682208?fromshare=blogdetail&sharetype=blogdetail&sharerId=80682208&sharerefer=PC&sharesource=m0_72696598&sharefrom=from_link
锁
java里的锁总结(synchronized隐式锁、Lock显式锁、volatile、CAS) - Life_Goes_On - 博客园
Java中锁机制,可以从synchronized隐式锁和lock显示锁展开。
synchronized隐式锁,它的锁对象对于实例同步方法是当前实例对象this,对于静态同步方法是该类的class对象,对于同步代码块是synchronized括号中的对象。它的原理是用过monitorenter和monitorexit实现。锁信息存储在java对象头中。锁性能损耗在多线程激烈竞争导致锁升级为重量级锁时,失败进程会挂起,涉及用户态与内核态切换。
关于锁升级synchronized锁状态会随着竞争情况升级,已降低性能开销。偏向锁:适用于一个线程访问同步块场景。该线程通过cas在对象头和栈帧中记录锁偏向的线程id。轻量级锁:当有另一个线程竞争,新线程通过cas自旋获取锁。重量级锁:自旋超过一定次数,未获得锁的线程会被阻塞放入等待队列,涉及系统内核mutex,会导致上下文切换较大开销。
Lock显式锁:jdk5之后有了显式锁,对锁的使用更加灵活但需要手动加锁释放,会有手误导致死锁风险。以及在上锁时放在trycatch外面,防止finally中无故释放锁。
可重入锁(reentrantLock):指一个线程在持有该锁的情况下可以再次成功获取到锁而不被阻塞。原理:锁内部维护一个持有计数器和一个持有线程标识。获取锁时计数器加1,释放-1,直到为0,锁完全释放。对于递归的synchronized方法避免死锁。
AQS
AQS,全称AbstractQueuedSynchronizer,是JUC并发包里一个构建锁和同步器的核心框架。像ReentrantLock、Semaphore这些常用的同步工具,底层都是基于AQS实现的。
它的核心思想是维护了一个volatile的int状态变量(state)和一个FIFO的线程等待队列。状态变量state的具体含义由子类决定,比如在ReentrantLock里它表示锁的重入次数,在Semaphore里就表示许可证的数量。
AQS定义了两种资源共享模式:独占模式(一次只有一个线程能执行,如ReentrantLock)和共享模式(多个线程可同时执行,如Semaphore)。
AQS使用了模板方法模式,它把线程入队、阻塞、唤醒这些复杂的通用逻辑都封装好了,只暴露出几个像tryAcquire、tryRelease这样的方法让子类去实现具体的资源获取和释放逻辑。这样,我们想实现一个自定义的锁,只需要继承AQS并重写这几个 protected 方法就可以了,非常方便。
简单来说,AQS通过一个状态变量和一个队列,为我们提供了一套管理线程同步和等待的成熟解决方案,是Java并发编程中一个非常基础和重要的组件
JMM的内存模型
JMM(Java 内存模型)详解 | JavaGuide
好的,我将从为什么,是什么,以及原理三方面介绍一下JMM。
JMM为什么存在是在并发编程中,多级缓存指令重排等技术,容易带来三个问题。
1、可见性问题:一个线程对共享变量的修改,另一个线程不能立即看到。
2、原子性问题:一个或多个操作,要么全部成功,要么都不执行。
3、有序性问题:程序执行的顺序可能被编译器或处理器为了优化而重新排序。
Java作为一种跨平台语言,需要一套自己的内存模型来屏蔽底层各种硬件和操作系统内存访问的差异,所以就有JMM。
JMM是什么,它是一种描述Java程序中各种变量(实例字段、静态字段等)的访问规则,以及在JVM中将变量存储到内存和从内存中取出变量的这样的底层细节。
JMM将内存系统分为主内存和工作内存。所有共享变量都存储在主内存中,每个线程有自己的工作内存,保存该线程使用到的主内存副本。线程对变量的读写等操作都必须在自己的工作内存中,线程间变量值只能通过主内存完成。(这也就是可见性问题的原因)
JMM如何解决这些问题。围绕前面三个特性,提供了一系列工具和规则。
原子性:JMM本身只保证基本数据类型(除long/double)的读写是原子的。对于更大范围的原子保证,依赖synchronized或者Lock接口的实现类(如ReetrantLock)。
可见性:JMM利用了volatile关键字和synchronized关键字。当一个变量被声明为volitale后,当线程修改此变量时,会立即将新值刷新回主内存。当线程读取此变量时,回强制从主内存重新读取最新的值。而synchronized是在释放锁之前,JMM会把工作内存中修改过的变量都刷新回主内存。
有序性:为了禁止编译器和处理器为了优化性能而进行的有害重排序,JMM也是利用了volatile关键字和synchronized关键字。volatile通过添加内存屏障来禁止指令重排序,synchronized同样通过锁机制,保证同一时刻只有一个线程执行同步代码,相当于线程串行执行。内存屏障,它可以理解为 CPU 指令,插入在指令之间,告诉编译器和 CPU,屏障前后的指令不能进行重排序。
最后还有一个核心原则:Happens-before。
无需任何同步手段,直接保证了某些操作的可见性。程序次序规则:一个线程内,前面的操作 happens-before 于后续的任意操作。 volatile 变量规则:对一个 volatile 变量的写操作 happens-before 于后面对这个变量的读操作。 传递性:如果 A hb B, B hb C,那么 A hb C。 Happens-before 原则本质上是 JMM 给程序员的一个强内存可见性承诺。只要操作间满足 happens-before 关系,那么 JVM 和底层硬件就会保证前一个操作的结果对后一个操作一定可见。
threadlocal
JVM
你好,我来介绍一下jvm。首先是jvm的内存结构,也叫运行时数据区域。分为线程私有和共享,私有的有程序计数器,虚拟机栈,本地方法栈。pc记录当前线程执行的字节码指令地址。虚拟机栈中有存储方法时调用的栈帧,包括局部变量表,操作数栈,方法出口等。本地方法栈是为Native方法服务。共享的堆,方法区。方法区以及永久代在jdk8及以后被元空间替代,直接使用本地内存。堆是java虚拟机中最大的一块,java中几乎所有实例对象都在里面初始化,也是垃圾回收进行的场所。所有堆也可以细分为新生代,老年代和永久代(或者8及以后的元空间)。
垃圾回收机制
分io密集型与cpu密集型,在java8及之前,是parOld 和cms parellal seriable
