【JAVA】java常见面试题——持续更新

目录

一、说一下Synchronized的底层原理

二、谈谈你对ThreadLocal的理解

三、什么是SpringBoot自动配置

四、在使用ThreadLocal时存在什么风险,有什么注意事项

五、Linux的常用命令

六、如何解决超卖问题

七、什么是分布式架构 

八、保证redis与数据库一致性的方法

九、socket了解吗?

十、说一下volatile?

十一、说一下CAS

十二、说一下AQS 

十三、Object的常用方法

 十四、抽象类和接口的区别

 十五、ArrayList和LinkedList的区别

十六、hashmap的底层结构

十七、ConcurrentHashMap的底层结构

十八、什么是反射

十九、反射使用的场景

二十、产生死锁的四个条件是什么

二十一、ReentrantLock和Synchronized区别

二十二、重写equals的步骤

二十三、线程与线程池的基本状态

二十四、线程的生命周期

二十五、start()和run()方法有什么区别

二十六、异常结构和理解

二十七、Lambda表达式的理解

二十八、为什么使用JSON

二十九、面向对象的理解

三十、Executor封装好的四种常用线程

三十一、线程池常见工作队列

三十二、为什么hashmap的长度要是2的幂次方

三十三、序列化接口是干什么的

三十四、状态模式和策略模式的区别

三十五、简单工厂和工厂模式的区别

三十六、ReentrantLock公平锁与非公平锁

三十七、对称加密和非对称加密的区别

三十八、synchronized 关键字的用法

三十九、什么是Class对象

四十、倒排索引

四十一、ES的高可用

四十二、ES的数据类型

四十三、Seata的XA模式和AT模式

四十四、synchronized的锁升级、锁消除、锁粗化

四十五、什么是守护线程

四十六、TreeMap的特点

四十七、什么是内存泄漏和内存溢出 

四十八、红黑树


一、说一下Synchronized的底层原理

Synchronized的话他底层是基于monitorentermonitorexit两个指令来实现的,因为每一个对象都拥一个monitor监视器,而线程就会通过Synchronized来获取这个监视器,当监视器被获取时,就会进去锁定状态。

当一个线程第一次获取monitor时,monitor的进入数就会从0变成1,当该线程再进获取时,monitor的进入数会再次加1。

但如果其他线程来获取时,发现monitor的进入数不为0,则会出现堵塞状况,直到进入数为0。

但在java6之前,Synchronized是完全依靠操作系统的互斥锁,会进行一个用户态到内核态的切换,所有的同步锁操作都是一个无差别的重量级操作,非常消耗资源。

所以在java6之后,会引入3种不同的monitor实现,为偏向锁,轻量级锁和重量级锁

偏向锁其实就是为了在单线程的情况下,完全不用考虑并发情况,所以不会涉及真正的互斥锁。不必浪费资源。但当有另一个线程来访问时,jvm就会从偏向锁转变为轻量级锁。

轻量级锁的话,虽然已经不是单线程的环境了,但这些线程会根据串行的方式来访问同一个加锁对象。但是还是会消耗重复加锁和解锁的性能开销。当正真发生并发情况时,就会切换为重量级锁。

重量级锁他是完全依靠操作系统互斥锁来实现的锁操作系统的互斥锁会实现线程的切换,从用户态到内核态,所以非常消耗资源。


二、谈谈你对ThreadLocal的理解

ThreadLocal是一个线程的局部变量,用于在线程中保存数据。ThreadLocal保存的数据只属于当前线程。每一个Thread都有自己的一个ThreadLocalMap用于数据存储。通过ThreadLocal将数据存入当前线程的ThreadLocalMap中,其中ThreadLocal作为键,存入的数据为值。

ThreadLocal的价值主要在于线程隔离,在该线程存入的数据也只属于该线程,该数据是其他线程不可见的,起到隔离作用。这样的话,在多线程并发环境下,就可以防止当前线程的值被其他线程所修改。相对于加同步锁,能够有效降低性能损失,提高并发的性能。


三、什么是SpringBoot自动配置

SpirngBoot自动配置就是不需要我们写代码,所有的配置都由SpringBoot自动完成。

springboot项目中启动类上都有一个@SpringBootApplication注解,主要是通过这个注解完成。

@SpringBootApplication中包含了三个注解@SpringBootConfiguration注解内部就是一个@Configuration注解,用于标记启动类为配置类。@ComponentScan注解是用于启动时扫描启动类所在包下以及子包下的所有bean的类,并注册到ioc容器中。@EnableAutoConfiguration是完成自动配置的核心注解,通过@Import注解导入AutoConfigurationImportSelector类,通过这个类的selectImports方法读取spring.factories文件,这个文件中包含了可以自动装配的所有类名,然后通过一系列(验证、去重、排除自动装配以及执行监听器等)操作后,将剩余的类名集合返回给IOC容器并将这些组件注册为bean。

SpringBoot最大特点就是他去除了各种xml配置文件改为application.yml进行统一的配置,并且按照阶梯的模式,简单明了,要使用的对象,则采取注解模式注入,省去大量代码。


四、在使用ThreadLocal时存在什么风险,有什么注意事项

可能发生内存泄漏因为ThreadLocalMap使用ThreadLocal的弱引用为key,如果ThreadLocal没有一个强引用来引用他,那么在下一次GC时势必会回收这个ThreadLocal,从而导致在ThreadLocalMap中存储的一个key为null的值,如果当前线程迟迟不结束的话,那么这个value就会一直存在一条强引用链,从而导致value无法回收,造成内存泄漏。

解决方案:在每次使用完ThreadLocal后都调用他的remove()方法,清除数据。


五、Linux的常用命令

ipconfig 查看当前ip地址

netstat 监控tcp/ip网络的一个工具,也能查看端口号。

top 查看当前进程信息。(实时更新)

ps 显示当前用户进程,可以通过管道符来过滤。

grep 搜索指定字符串内容。

vim编辑器 用于创建和编写文本。

kill 进程杀死命令。


六、如何解决超卖问题

使用悲观锁,每次访问都要先获取锁。

使用乐观锁,可以设置一个version字段,读取数据时每次将version一起读出,每次更新数据时version+1,并判断数据库中的version值与自己读到的version值是否一致,如果一致,则更新,否则取消更新。

通过请求队列,强制将多线程变为单线程。

通过数据库事务。先更新,在查询,发现库存已经不足,则回滚。

使用redis实现分布式锁

使用spring事务


七、什么是分布式架构 

分布式架构就是将一个系统给他拆分成多个,就比如电商系统,可以拆分成功订单系统、商品系统、会员系统、支付系统等等,并且还可以继续拆分。

这样做的好处就是:

        可以降低代码的耦合性,对于一个团队来说每个人只需要维护、测试好自己的系统,自己模块,不用担心代码冲突等,能够做到更好团队合作来完成一个项目。


八、保证redis与数据库一致性的方法

使用事务和回滚:只有当所有操作正常完成才提交事务,否则回滚。

分布式锁:可以保证只有一个线程来对redis和数据库进行写操作。防止并发导致数据数据不一致问题。

定时同步数据:通过一个任务调度来周期性的检查数据是否同步,并将redis中的数据同步至数据库。确保数据的一致性。

发布/订阅模式:可以用于实现数据库与redis的实时更新同步。


九、socket了解吗?

socket套接字,他是一个用于网络通信的一个API接口。他的话就是在一个应用程序进行数据传输时,会通过socket来建立一个远程连接,而socket内部是通过Tcp/Ip协议来把数据传输到网络。

一般在java进行Tcp编程时,会用到Socket模型,服务器用ServerSocket来监听端口,客户端通过Socket来连接服务器,服务端用accept()方法来接收并返回Socket实例,双方通过Socket来打开输入/输出流来进行读写数据。


十、说一下volatile?

首先volatile是一个关键字,当他作用余一个变量时,可以使这个变量具有可见性有序性。因为当对一个volatile变量执行写入时,jvm会将对应的共享变量立即刷新到主存,当读取这个变量时,也会从主存中进行读取。有序性是通过happens-before原则来保证的。.


十一、说一下CAS

CAS指的是compare and swap,即比较和交换。他的话本质是一种无锁的线程同步解决方案,基于乐观锁思想的操作。就是比如说在你需要更新一个变量时,先看一下这个变量的值并记住,然后在你真正要改时,再次看一下这个变量的值是否和上次一样,如果一样则进行更新,否则说明已经被其他线程修改,则不更新所以并没有实现加锁操作。

在java中AtomicIntegerconcurrentHashMap都是基于CAS机制来实现的。

CAS的其实是通过Unsafe类提供compareAndSwapXXX方法来实现的,底层则是通过这个方法来调用cpu的一个指令cmpxchg。这个指令连续执行,不会被打断,所以可以保证原子性。

缺点

        循环时间长,开销大。
               
Unsafe的实现中使用了自旋锁机制,如果cas操作失败,则会循环进行操作。如果长时间都不成功的话,则会对cpu有很大的开销。

        只能保证一个共享变量的原子操作。

        ABA问题。
               
就是如果线程1想要改变一个值时,先看为A,然后线程2将这个值改为了B,然后又改为了A。当线程1真正修改这个值发现这个值还是A没有被修改,则会继续执行,进行修改。
                可以通过AtomicStampedReference类使用compareAndSet方法来解决该问题。


十二、说一下AQS 

AQS的全名为AbstractQueuedSynchronizerReentrantLock就是基于AQS来实现的,她可以说是一种提供原子式管理同步状态、阻塞和唤醒线程等功能,并提供等待队列模型的线程同步框架。

他的核心思想就是,如果请求的资源空闲,那么就将当前请求资源的线程设置为有效的工作线程,并将资源设置为锁定状态,如果资源被占用,则会实现等待机制,就是将获取不到锁的线程加入到等待队列中,由CLH变体的FIFO双向队列,并用一个由volatile修饰的int类型的state来表示其同步状态,通过cas来完成对state的改变,来实现多线程的独占模式(state=0)共享模式(state>0同一时刻可以多个线程获取同步资源)。 


十三、Object的常用方法

  1. clone() 
  2. equals() 
  3. finalize()
  4. getClass()
  5. hashCode()
  6. notify()
  7. notifyAll()
  8. toString()
  9. wait()

 十四、抽象类和接口的区别

抽象类的话是通过abstract class来定义,接口则是通过interface

抽象类是通过子类用extents来继承,子类只能继承一个父类,接口则是通过implements来实现,类可以实现多个接口

抽象类的话可以有构造方法接口没有构造方法

抽象类既可以定义抽象方法也可以定义普通方法接口只能定义抽象方法,但在java8开始可以用default 来定义默认方法

抽象类的方法可以是任意修饰符接口则只能是public修饰


 十五、ArrayList和LinkedList的区别

ArrayList的底层是Object数组LinkedList的底层是双向链表

ArrayList主要用于查找和修改,LinkedList主要用于插入和删除


十六、hashmap的底层结构

hashmap线程不安全的,他的底层结构是数组+链表+红黑树,他存储的是键值对,这个键值对会被封装在一个Entry对象中,这个对象中有key、value、next、hash。在一个数组中保存一个个Entry对象Node<K,V>类实现Entry接口)。

一般的话,hashmap的初始容量是16,当每次存储元素时,都会根据key值来计算出hash值,通过(长度-1)&hash来算出要存放在数组中的位置下标,如果该位置已经有其他元素,则会以链表的方式存储,通过next来指向新添加的元素,形成链表(时间复杂度O(n)。

链表长度大于8并且数组长度大于64时链表将会转化为红黑树(时间复杂度O(logn))TreeNode<K,V>类实现Entry接口),为了减少搜索时间。 


十七、ConcurrentHashMap的底层结构

concurrentHashMap是一个线程安全的且效率比较高的Map,在jdk1.7之前是由分段数组和链表组成,而在jdk1.8改为了数组、链表和红黑树。底层通过Synchronized和cas来实现。


十八、什么是反射

在运行期间能够能够直接操作程序内部的属性和方法,会破坏封装。

比如说,在运行期间获取任意一个对象所属的类,或者是构造任意一个对象获取或调用任意一个对象的成员变量和方法


十九、反射使用的场景

一般的反射用的比较多的都是一些底层框架中,比如spring框架、MyBatis框架。

JDBC连接数据库时、使用Class.forName(),通过反射来加载数据库的驱动类。

Spring中的IOCAOP底层也是使用反射实现。

MyBatis框架中Mapper接口代理对象的创建,也是用反射实现。


二十、产生死锁的四个条件是什么

互斥条件:该资源任意一个时刻只有一个线程占用

请求与保持条件:一个进程因请求资源堵塞时,对以获得的资源保持不放

不剥夺条件:线程获得资源在未使用完之前不能被其他线程强行获取

循环等待条件:若干进程之间形成一种头尾相接的循环等待关系


二十一、ReentrantLock和Synchronized区别

两者都是可重入锁,自己可以再次获取自己的内部所。

synchronized的话,他是通过底层moniter指令来实现的,reentrantLock则是通过jdkApi

reentrantLock还可以等待可中断,就是可以中断正在等待的线程和可实现公平锁,默认是非公平锁。(公平锁是指先等待的线程先获取锁)synchronized只能是非公平锁

使用场景:

synchronized 适用于:

  • 简单的同步需求。
  • 对性能要求较高的场景。
  • 不需要复杂的同步机制,例如读写锁、公平锁等。

ReentrantLock 适用于:

  • 复杂的同步需求,例如读写锁、公平锁等。
  • 需要可中断、定时和公平的锁等特性的场景。
  • 需要更加灵活地控制线程的等待和唤醒的场景。

二十二、重写equals的步骤

    @Override
    public boolean equals(Object otherObject) {
        //1.先比较地址是否相等 这样开销最小
        if (this == otherObject) return true;
        //2.如果比较的对象会null 则直接返回false
        if (otherObject == null) return false;
        //3.检测比较的对象与当前对象是否属于同一个类 不属于则返回false
        if (this.getClass() != otherObject.getClass()) return false;
        //4.转化为特定类型
        MyEqual myEqual = (MyEqual) otherObject;
        //5.最终进行字段的比较
        return myEqual.id == id && Objects.equals(myEqual.name, name);
    }

        //6.重写hashcode方法
    @Override
    public int hashCode() {
    	//不要忽略,如果这里返回一个常量 将导致所有比较都相等了
        return Objects.hash(id, name);
    }

二十三、线程与线程池的基本状态

线程:

NEW(初始状态)、READY(就绪状态)RUNNABLE(运行状态)、BLOCKED(堵塞状态)、WAITING(等待状态)、TIME_WAITING(超时等待状态)、TERMINATER(终止状态)

线程池:

RUNNING(运行状态)、SHUTDOWN(不在接受新任务)、STOP(不在接受新任务、停止所有任务)、TIDYING(所有任务全部停止)、TERMINATED(终止状态)


二十四、线程的生命周期

首先是线程被创建后处于NEW初始状态,通过start()方法后进入READY就绪状态,等待JVM调度,当获取时间片后,会进入RUNNABLE运行状态,如果执行wait()方法,则会进入WAITING等待状态,需要其他线程通过notify或notifyAll唤醒,也可以通过wait()方法或sleep()方法设置时间参数,来进入TIME_WAITING超时等待状态,等到时间结束就会回到RUNNABLE运行状态,当线程调用同步方法时,没有获取到锁的情况下会进入BLOCKED堵塞状态线程结束后会进入TERMINATED终止状态


二十五、start()和run()方法有什么区别

当调用start()方法后,线程会被放入等待队列,等待JVM调度,当获取CPU时间后,线程会调用run()方法,执行业务逻辑。

start()方法用于启动线程run()方法则是用于封装业务逻辑代码。当new 一个Thread对象后需要重写run()方法。


二十六、异常结构和理解

Throwable是所以异常的父类、他一共有两个子类,Error类和Exception类。

Error一般是系统内部错误,通常是JVM虚拟机异常,StackOverflowError(栈溢出)和OutOfMemoryError(堆溢出)。

Exception运行时异常Exception中还分为两种异常类型。

一种是RuntimeExceptionRuntimeException子类,这种是检查型异常,一般需要使用trycatch处理,否则报错。NullPointerException(空指针异常)、IndexOutOfBoundsExceptin(下标越界异常)。

另一种是Excaption类或Excaption子类,为非检查型异常,不需要处理。IOException(IO读写异常)、SQLException(SQL异常)。


二十七、Lambda表达式的理解

Lambda表达式他主要就是简写代码,还能进行java性能提升

一般可以用于替代匿名内部类,比如说新建一个线程就可以new Thread()->().start();

对集合进行迭代,forEach遍历。

还能通过Stream流进行一些过滤、排序等操作。


二十八、为什么使用JSON

简单易用:JSON的语法简单,易于理解和编写,可以快速地进行数据交换。

跨平台支持:JSON可以被多种编程语言解析和生成,可以在不同的平台和语言之间进行数据交换和传输。

数据交换格式:JSON是一种标准的数据交换格式,可以在Web应用程序中广泛使用,如前后端数据交互、API接口数据传输等。

轻量级:JSON的数据格式轻量级,传输数据时占用带宽较小,可以提高数据传输速度。

易于扩展:JSON的数据结构灵活,支持嵌套对象和数组等复杂的数据结构,便于扩展和使用。

安全性:JSON数据格式是一种纯文本格式,不包含可执行代码,不会执行恶意代码,因此具有较高的安全性。


二十九、面向对象的理解

面向对象就是万物皆可对象,每个对象都有每个对象的作用,并且对象直接是有相互联系的,每个对象都有自己的属性,方法。

主要分为三个模块

继承:就是子类继承父类,让子类拥有父类的方法和属性,能够减少代码的冗余。

封装:就是隐藏对象的属性和实现细节,只能通过对外开放的方法来获取或修改被隐藏的属性。

多态:是代码有更好的拓展性,一般体现在重写重载向上转型(父类实例引用子类对象)、向下转型(将父类转换为子类)


三十、Executor封装好的四种常用线程

newFixedThreadPool(固定数目线程的线程池):

  • 核心线程数和最大线程数一样,
  • 非核心线程存活时间为0,
  • 堵塞队列为无界队列LinkedBlockingQueue(可设置容量队列)

        一般适用于执行长期任务,尽可能少分配长执行。

newCachedThreadPool(可缓存线程的线程池):

  • 核心线程数为0
  • 最大线程数为Integer.MAX_VALUE
  • 堵塞队列为SynchronousQueue(同步队列)
  • 非核心线程存活60秒

        一般用于并发量大执行时间短的小任务

newSingleThreadExcutor(单线程的线程池):

  • 核心线程数为1
  • 最大线程数为1
  • 堵塞队列为LinkedBlockingQueue(可设置容量队列)
  • 非核心线程存活时间为0

        一般用于串行执行任务,一个一个执行。 

newScheduledThreadPool(定时及周期执行的线程池):

  • 最大线程数为Integer.MAX_VALUE
  • 堵塞队列为DelayedWorkQueue(延迟队列)
  • 非核心线程存活时间为0
  • scheduleAtFixedRate():按某种速率周期执行
  • scheduleWithFixedDelay():在某个延迟后执行

        一般用于周期性执行任务场景。 


三十一、线程池常见工作队列

ArrayBlockingQueue(有界队列):数组实现、按FIFO排序。

LinkedBlockingQueue(可设置容量队列):链表实现,FIFO排序,可自己设置容量,不设置则无边界。

DelayedWorkQueue(延迟队列):任务定时周期延迟执行的队列,根据指定的执行时间从小到大排序。

PriorityBlockingQueue(优先级队列):具有优先级的无界队列。

SynchronousQueue(同步队列):不存储元素,每个插入操作必须等另一个线程调用移除操作,否则插入操作一直阻塞。


三十二、为什么hashmap的长度要是2的幂次方

首先,如果是数组长度是2的幂次方的话,不易产生hash冲突。

一般计算下标时用(n-1)&hash,如果是2的幂次方的话,n-1的最低位就都是1,可以保证计算出的下标分布均匀。


三十三、序列化接口是干什么的

用于对象的序列化,在类上实现Serializable接口

序列化就是将对象转化为字节流的过程

序列化的使用情况:

  •         内存中的对象需要保存在物理文件或者数据库中
  •         使用Socket套接字在网络上传输对象
  •         深拷贝时

三十四、状态模式和策略模式的区别

状态模式重点在各状态之间的切换,从而做不同的事情;

而策略模式更侧重于根据具体情况选择策略,并不涉及切换。

状态模式封装了对象的状态,而策略模式封装算法或策略。


三十五、简单工厂和工厂模式的区别

简单工厂的话他又称为静态工厂模式,就是它可以根据传入不同的参数来返回不同的实例。

工厂模式的话,工厂父类负责创建产品的公共接口,工厂子类则是负责生成具体的产品对象。


三十六、ReentrantLock公平锁与非公平锁

NonfairSync 类继承了 Sync类,表示采用非公平策略获取锁:每一次都尝试获取锁,不会按照公平等待的原则进行等待,不会让等待时间最久的线程获得锁。

FairSync类也继承了 Sync类,表示采用公平策略获取锁:当资源空闲时,它总是会先判断 sync队列是否有等待时间更长的线程,如果存在,则将当前线程加入到等待队列的尾部,实现了公平获取原则。 

非公平锁性能比公平锁高5~10倍,因为公平锁需要频繁唤醒队列中的线程,比较消耗资源


三十七、对称加密和非对称加密的区别

对称加密是指加密和解密使用相同的密钥的加密算法。他的原理就是将明文通过密钥进行加密,然后再将加密后的密文发送出去。接受方接受到密文后,再用相同的密钥解密。

非对称加密是指加密和解密使用不同的密钥的加密算法。他的原理的话就是发送方用接收方的公钥加密,发送给接收方,接受用自己私钥解密。   

密钥交换算法是在双方不直接传递密钥的情况下完成密钥交换。他的原理就是我们把a看成甲的私钥,A看成甲的公钥,b看成乙的私钥,B看成乙的公钥,DH算法的本质就是双方各自生成自己的私钥和公钥,私钥仅对自己可见,然后交换公钥,并根据自己的私钥和对方的公钥,生成最终的密钥secretKey,DH算法通过数学定律保证了双方各自计算出的secretKey是相同的。 


三十八、synchronized 关键字的用法

修饰实例方法:synchronized修饰实例方法, 则用到的锁,默认为this当前方法调用对象(this

修饰静态方法:synchronized修饰静态方法, 则其所用的锁,默认为Class对象(this.getClass()

修饰代码块:synchronized修饰代码块, 则其所用的锁,是某个指定Java对象(自定义对象


三十九、什么是Class对象

Class对象的话他是JVM用来保存类对象信息的,一般的话在java的编译期间会将源码文件编译成.class 字节码文件,编译器的话也会同时在这个文件中生成Class对象。一般在加载阶段就会通过类加载机制将Class对象加载到内存中。

一般可以通过三种方式获取Class对象类名.Class、Class.forName()、getClass()。


四十、倒排索引

正向索引是最传统的,根据id索引的方式。但根据词条查询时,必须先逐条获取每个文档,然后判 断文档中是否包含所需要的词条,是根据文档找词条的过程。

而倒排索引则相反,是先找到用户要搜索的词条,根据词条得到保护词条的文档的id,然后根据id 获取文档。是根据词条找文档的过程。


四十一、ES的高可用

Elasticsearch(ES)是一个开源的分布式搜索引擎,它提供了高效的全文搜索、分析和数据可视化功能。在实际应用中,ES的高可用性是非常重要的,因为它通常用于处理大量的数据和请求。

ES高可用性的实现主要依赖于以下三个方面:

        分布式架构:

ES采用分布式架构,将数据分散存储在多个节点上,每个节点都可以处理请求和响应。这种架构可以提高系统的可扩展性和容错性,因为当一个节点出现故障时,其他节点可以继续工作,保证系统的正常运行。

        数据复制:

ES采用数据复制机制,将数据复制到多个节点上,以保证数据的可靠性和可用性。当一个节点出现故障时,其他节点可以继续提供服务,因为它们都有相同的数据副本。此外,ES还提供了主从复制机制,其中一个节点作为主节点,负责处理写操作,其他节点作为从节点,负责处理读操作。这种机制可以提高系统的性能和可用性。

        负载均衡:

ES采用负载均衡机制,将请求分发到多个节点上,以提高系统的性能和可用性。负载均衡可以根据节点的负载情况和网络延迟等因素,动态地调整请求的分发策略,以保证每个节点都能够处理适当的请求量。


四十二、ES的数据类型

String 类型

date 时间类型 

复杂类型Array、object、nested

GEO 地理位置类型


四十三、Seata的XA模式和AT模式

XA模式:

        他是有一个事务协调者,会通知每个事务参与者去执行本地事务,然后事务参与者执行完后会返回给事务协调者执行状态,如果全部成功则提交事务,如果有一个失败就会所有回滚。

AT模式:

        他是每个事务都会记录一个数据快照日志,执行完事务就直接提交,返回事务的状态,如果成功会删除日志,如果失败,则会根据日志回滚到更新前。

在AT模式可能会发生脏写问题,所有引入了全局锁的概念在释放DB锁之前,先拿到全局锁,避免同一时刻有另外一个事务来操作当前数据。

事务1先获取DB锁,保存快照,然后执行sql,然后在获取全局锁,提交事务,释放DB锁,这个时候如果有事务2,则他会获取DB锁,执行sql,在获取全局锁,发现获取失败,会重试默认30次间隔10毫秒,最终还没获取成功则事务回滚。

AT模式与XA模式最大的区别:

        XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源。

        XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。

        XA模式强一致;AT模式最终一致


四十四、synchronized的锁升级、锁消除、锁粗化

锁升级

        jvm将synchronized 锁分为无锁、偏向锁、轻量级锁、重量级锁四种状态。从无锁到重量级锁的转变过程叫做锁升级。

锁消除

        锁消除是程序在运行期间,jvm做出的优化手段,在编译期执行,编译器和jvm会检测当前代码是否是多线程执行或者是否有必要加锁,如果没必要,但是又把锁写了,就会在编译过程中把锁去掉。

锁粗化

        synchronized所包含的代码越多,则粒度越大,锁粗化就是如果某个场景频繁加锁解锁,此时编译器就会将这些加锁的代码优化成一个加锁操作,即增加了锁的粒度,就是锁的粗化。


四十五、什么是守护线程

守护线程是在后台提供一种通用服务的线程,比如说垃圾回收线程,一般守护线程在所有非守护线程结束后,守护线程也会自动终止。


四十六、TreeMap的特点

TreeMap特点:

  • 不可以存储重复的数据(当Key值相同时,Value值会新值覆盖旧值)
  • Key和Value不能存储NULL值
  • 存储的数据有序(默认自然顺序)

TreeMap是线程不安全的,如果多线程访问,会导致抛出ConcurrentModificationException的异常。线程安全使用HashTable(现使用较少),ConcurrentHashMap或使用工具类进行线程同步。


四十七、什么是内存泄漏和内存溢出 

1、内存泄漏(memory leak ):指程序申请内存后,无法释放已申请的内存空间,导致系统无法及时回收内存分配给其他程序使用。

        ThreadLocal:用完后不remove。

2、内存溢出(out of memory):指程序申请内存时,没有足够的内存提供给申请者使用,导致无法正常存储到内存中。例如给了一个int类型的存储空间,却存储long型数据就会发生内存溢出。

        栈溢出;递归。

        堆溢出;不断的创建对象。


四十八、红黑树

红黑树是一种自平衡的二叉查找树,它通过下列规则,保持二叉树的平衡性。

  1. 任何一个结点都有颜色,黑色或者红色;
  2. 根结点是黑色的;
  3. 父子结点之间不能出现两个连续的红色结点(每个红色结点的两个子结点都是黑色);
  4. 任何一个结点向下遍历到叶子结点,所经过的黑结点个数必须相等(相同的黑色高度);
  5. 每个叶子结点都是黑色的空结点(NIL 结点);

注:本篇文章都是我自己的理解,可能用词和语句不够严谨,如有错误请评论指正,谢谢!(持续更新中......)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值