To Fu~


Java中常见的集合有那些?

ArrayList:动态数组列表,可根据需要自动调整大小。

LinkedList:双向链表,在插入和删除元素时具有较好的性能。

HashSet:基于哈希表实现的集合,不允许存储重复元素。

TreeSet:基于红黑树实现的有序集合,元素按照自然顺序或自定义比较器进行排序。

HashMap:基于哈希表实现的键值对映射集合。

TreeMap:基于红黑树实现的有序键值对映射集合,按键的自然顺序或自定义比较器进行排序。

LinkedHashMap:具有链式访问顺序的哈希映射,维护插入顺序或访问顺序。

ArrayDeque:双端队列,支持在队头和队尾进行高效的元素添加和删除。

ArrayList和LinkedList的区别

ArrayList是基于动态数组:

ArrayList提供了快速的随机访问性能高效遍历的场景,通过索引可以直接访问数组中的元素。但在插入和删除元素时,可能需要移动其他元素以保持数组的连续性,因此在大规模插入和删除操作时效率可能较低。

LinkedList是基于双向链表:

LinkedList适用于频繁插入和删除元素的场景。在实际使用中,根据具体需求选择合适的集合类型可以提高程序的性能和效率。

HashMap的底层原理,扩容机制?

扩容的具体过程如下:

计算新的容量:新的容量为原来的容量乘以 2。

创建新的哈希表:创建一个新的HashMap对象,容量为新的容量。

遍历旧的哈希表:遍历旧的哈希表,将每个键值对复制到新的哈希表中。

重新计算哈希值和索引位置:对于每个键,重新计算它的哈希值,并将其与新的容量进行取模运算,得到新的索引位置。

处理哈希冲突:如果新的索引位置上已经有其他键值对,那么就将这个键值对添加到链表的头部。

更新引用:将原来的HashMap对象的引用指向新的HashMap对象。

HashMap,HashTable,CurrentHashMap的区别

HashMap是 Java 中最常用的哈希表实现之一,它的底层原理基于哈希表的思想,即通过哈希函数将键映射到一个整数索引,然后将值存储在对应索引的位置上。HashMap的性能较好,适用于大量插入和删除操作的场景。但是,由于HashMap不是线程安全的,因此在多线程环境中使用需要进行额外的同步处理。

HashTable是 Java 早期提供的一个哈希表实现,它与HashMap类似,但是HashTable是线程安全的。在HashTable中,所有的方法都是同步的,因此在多线程环境中使用不需要进行额外的同步处理。但是,由于HashTable的所有方法都是同步的,因此它的性能较差,不适用于大量插入和删除操作的场景。

CurrentHashMap是 Java 5 中引入的一个哈希表实现,CurrentHashMap是线程安全的,并且在多线程环境中性能较好。CurrentHashMap使用了分段锁的技术,将整个哈希表分成多个段,每个段都可以独立地进行读写操作。因此,在多线程环境中,只有在访问同一个段时才需要进行同步处理,从而提高了性能。

线程的实现方式?

继承Thread类,重写run方法

实现Runnable接口,实现run方法

实现Callnable接口,实现call方法

利用ExecutorService线程池的方式创建线程

线程状态有那些,通过那些方法进行切换?

  • 新建(New):线程创建后尚未开始执行。
  • 就绪(Runnable):线程已准备好执行,但还未被调度执行。
  • 运行(Running):线程正在执行。
  • 阻塞(Blocked):线程被阻塞,无法继续执行。
  • 等待(Waiting):线程正在等待某个事件发生。
  • 超时等待(Timed Waiting):线程正在等待某个事件发生,但有超时限制。
  • 终止(Terminated):线程已执行完毕。

线程状态的切换可以通过以下方法进行:

  • start():将线程从新建状态转换为就绪状态。
  • join():将线程从运行状态转换为等待状态,直到其他线程执行完毕。
  • yield():将线程从运行状态转换为就绪状态,让其他线程有机会执行。
  • sleep(long millis):将线程从运行状态转换为超时等待状态,指定时间后恢复为就绪状态。
  • wait():将线程从运行状态转换为等待状态,直到其他线程唤醒。
  • notify():将线程从等待状态转换为就绪状态。
  • notifyAll():将所有等待状态的线程转换为就绪状态

线程池,线程池的核心七大参数,什么时候执行拒绝策略

  1. 核心线程数
  2. 最大线程数
  3. 空闲线程存活的时间 ->空闲线程
  4. 空闲线程存活时间单位
  5. 工作队列
  6. 线程工厂
  7. 拒绝策略

什么时候执行拒绝策略

比如核心线程数:5,最大线程数:8,队列长度是:5  

--->5核心线程数量用完,---->再来任务-->放到队列-->队列满了-->创建临时线程(非核心线程:最大线程数-核心线程数据=8-5=3) 可以接收3个任务;-->然后3个线程用完-执行决绝策略

常见的拒绝策略有四种

    1. 直接抛出异常,阻止程序运行
    2. 使用调用者所在的线程来执行任务。如果线程池已经关闭,则丢弃该任务
    3. 丢弃任务队列中最旧的任务,并将新任务添加到队列
    4. 直接丢弃新任务,不做任何处理。

核心线程数据+对列长度+(最大线程数-核心线程数)

Java中常见的设计模式?

(共有23种)

  1. 单例模式(饿汉,懒汉):确保一个类只有一个实例,并提供全局访问点。
  2. 原型模式:每次拿都是原型
  3. 工厂模式(Factory Pattern):定义一个创建对象的接口,将对象的创建与使用分离。
  4. 建造者模式(Builder Pattern):将一个复杂对象的构建与表示分离,使同样的构建过程可以创建不同的表示。
  5. 原型模式(Prototype Pattern):通过复制现有的对象来创建新的对象。
  6. 策略模式(Strategy Pattern):定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
  7. 适配器模式(Adapter Pattern):将一个类的接口转换成客户期望的另一个接口,使原本不兼容的类可以一起工作。
  8. 装饰器模式(Decorator Pattern):动态地给一个对象添加一些额外的职责。
  9. 代理模式(Proxy Pattern):为其他对象提供一种代理以控制对这个对象的访问。

spring框架的ioc?DI注入的方式有那些?

在 Spring 框架中,IOC(Inversion of Control,控制反转)是一种设计思想,它将对象的创建和管理交给容器来处理,而不是在代码中直接创建和管理对象。这种方式可以提高代码的可维护性和可扩展性,同时也可以减少代码的冗余和耦合。

DI(Dependency Injection,依赖注入)是 IOC 的一种实现方式,它通过在容器中配置对象的依赖关系,然后在需要的时候将这些依赖注入到对象中。DI 可以让对象之间的依赖关系更加清晰,同时也可以让代码更加简洁和易于维护。

DI 注入的方式有以下几种:

  1. 构造注入:通过构造函数将依赖注入到对象中。
  2. 属性注入:通过属性赋值将依赖注入到对象中。
  3. 接口注入:通过接口将依赖注入到对象中。
  4. 方法注入:通过方法将依赖注入到对象中。

Spring的事务?事务的传播行为有那些?

  1. REQUIRED(默认):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新事务。
  2. SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。
  3. MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  4. REQUIRES_NEW:总是创建一个新事务,如果当前存在事务,则将当前事务挂起。
  5. NOT_SUPPORTED:总是以非事务方式执行,如果当前存在事务,则将当前事务挂起。
  6. NEVER:总是抛出异常,如果当前存在事务,则将当前事务挂起。
  7. NESTED:如果当前存在事务,则创建一个嵌套事务;如果当前没有事务,则以非事务方式执行。

这些传播行为可以通过在事务方法上添加@Transactional注解,并设置propagation属性来指定

Redis 支持的数据类型有哪些?有哪些应用场景?什么是缓存穿透?怎么解决?

Redis 是一种基于键值对的 NoSQL 数据库,它支持多种数据类型,包括字符串、列表、集合、有序集合、哈希表等。Redis 的应用场景非常广泛,包括缓存、消息队列、计数器、排行榜、分布式锁等

缓存穿透产生原因:

  • 原来数据是存在的,但由于某些原因(误删除、主动清理等)在缓存和数据库层面被删除了,但前端或前置的应用程序依旧保有这些数据;
  • 恶意攻击行为,利用不存在的Key或者恶意尝试导致产生大量不存在的业务数据请求。

导致:

客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会访问数据库。导致DB的压力瞬间变大而卡死或者宕机。

大量的高并发的请求打在redis上
这些请求发现redis上并没有需要请求的资源,redis命中率降低
因此这些大量的高并发请求转向DB请求对应的资源
DB压力瞬间增大,直接将DB打垮,进而引发一系列“灾害”

解决方案:

        接口校验:

                对唯一id不规范的无效访问直接拦截

        对空值进行缓存:

                虽然数据库中没有该id的用户的数据,但是在redis中对其进行缓存,这样当请求到达redis的时候就会直接返回一个null的值给客户端,避免了大量无法访问的数据直接打在DB上。

注意:

  • key设置的过期时间不能太长,防止占用太多redis资源,设置一个合适的TTL,比如两三分钟。
  • 当遇到黑客暴力请求很多不存在的数据时,就需要写入大量的null值到Redis中,可能导致Redis内存占用不足的情况。
使用布隆过滤器:

布隆过滤器结果(有一定容错率):

                不存在的就一定不存在,存在的也不一定存在!

解释一下什么是 aop?有那些使用场景?

AOP(Aspect Oriented Programming)即面向切面编程,它是一种编程思想,旨在将横切关注点与业务逻辑分离,以提高代码的可读性、可维护性和可扩展性。AOP 通过动态代理技术在运行时动态地织入横切关注点的代码,从而实现对业务逻辑的增强。

AOP 的使用场景包括日志记录、性能监控、事务管理、权限验证、异常处理等。通过使用 AOP,可以将这些横切关注点的代码从业务逻辑中分离出来,形成独立的模块,从而实现代码的复用和关注点的分离。

synchronized 和 volatile 的区别是什么?

  synchronized 线程安全--本地-->JVM

  Synchronized 修饰方法,代码块,volatile修饰变量

  Synchronized 同一时刻,只能有一个线程持有锁,保证原子性

  Volaite 不能保证原子性,但一旦修改,其它线程立即可见

表达:synchronized 适用于保护临界区,确保同一时间只有一个线程访问共享资源;

而 volatile 适用于标记被多个线程共享的变量,确保其对所有线程的可见性。

如何决定使用 HashMap 还是 TreeMap?

在 Java 中,HashMapTreeMap都是常用的集合类,它们都可以用于存储键值对。但是,它们的实现方式和性能特点有所不同,因此在选择使用HashMap还是TreeMap时,需要考虑以下几个因素:

  • 数据结构HashMap是基于哈希表实现的,而TreeMap是基于红黑树实现的。哈希表的查找速度非常快,但是插入和删除操作的性能可能会受到哈希冲突的影响。红黑树的查找、插入和删除操作的性能都比较平衡,但是空间复杂度比哈希表高。
  • 键的顺序HashMap不保证键的顺序,而TreeMap按照键的自然顺序进行排序。如果需要按照键的顺序进行遍历或查找,那么应该使用TreeMap
  • 性能要求:如果对性能要求比较高,那么应该选择HashMap,因为它的查找速度非常快。如果对性能要求不是很高,但是需要按照键的顺序进行遍历或查找,那么可以选择TreeMap
  • 数据量:如果数据量比较小,那么可以选择HashMap,因为它的空间复杂度比较低。如果数据量比较大,那么可以选择TreeMap,因为它的空间复杂度比较高,但是可以保证键的顺序。

防止数据重复提交的方法?

  1. 生成唯一标识符(Token):在用户请求表单时生成一个唯一的标识符(Token),将其包含在表单中。当表单提交时,检查Token是否已经被使用过,如果已经被使用则拒绝提交。

  2. 表单重定向:在用户提交表单后,将其重定向到一个新的页面,而不是直接返回原页面。这样即使用户点击浏览器的刷新按钮,也不会再次提交表单。

  3. 双重提交检查:在服务器端记录每个提交的表单标识符,并在处理每个请求时检查该标识符是否已经被使用过。

  4. 使用JavaScript禁用提交按钮:在用户点击提交按钮后,使用JavaScript禁用提交按钮,防止用户多次点击。

  5. 使用验证码:要求用户在提交表单之前输入验证码,以确保每个提交都是由真实用户进行的。

ThreadLocal 是什么?有哪些使用场景?

ThreadLocal 是 Java 中的一个类,它提供了一种在多线程环境中存储线程局部变量的机制。ThreadLocal 变量与普通变量的区别在于,每个线程都有自己的 ThreadLocal 变量副本,这些副本之间互相隔离,不会相互影响。

ThreadLocal 的使用场景主要包括以下几个方面:

  • 线程安全的单例模式:在单例模式中,如果需要保证线程安全,可以使用 ThreadLocal 来创建线程局部的单例对象,每个线程都有自己的单例对象副本,不会相互影响。
  • 线程间通信:在多线程环境中,如果需要在不同的线程之间传递数据,可以使用 ThreadLocal 来存储线程局部的变量,然后在不同的线程中访问这些变量。
  • 性能优化:在一些性能敏感的场景中,可以使用 ThreadLocal 来避免频繁的上下文切换和锁竞争,从而提高系统的性能。

需要注意的是,ThreadLocal 变量虽然在每个线程中都有自己的副本,但是这些副本之间是相互隔离的,不会相互影响。因此,如果需要在不同的线程之间共享数据,不能使用 ThreadLocal。

== 和 equals 的区别是什么?

(==):

基本数据类型下: 比较值  引用数据类型下:比较地址

elulas:

只比较引用数据类型  比较的内容按方法内部重写而定

方法重写:

  1. 子类可以重写父类中的方法,但是不能重写父类中私有的方法。
  2. 子类重写父类的方法时,方法的返回类型、方法名和参数列表必须与父类中的方法完全相同。
  3. 子类重写父类的方法时,可以修改方法的访问修饰符,但是不能比父类中的方法更严格。
  4. 子类重写父类的方法时,可以添加新的异常处理,但是不能抛出比父类中方法更多的异常。

方法重载:

  1. 方法名必须相同。
  2. 参数列表必须不同,包括参数的类型、个数和顺序。
  3. 方法的返回类型可以相同也可以不同。
  4. 方法的修饰符可以相同也可以不同。

String 类的常用方法都有那些?

String 类是 Java 中最常用的类之一,提供了许多常用的方法来处理字符串。以下是 String 类的一些常用方法:

  1. charAt(int index):返回指定索引处的字符。

  2. length():返回字符串的长度。

  3. substring(int beginIndex):返回从指定索引开始到字符串末尾的子字符串。

  4. substring(int beginIndex, int endIndex):返回从指定开始索引到指定结束索引的子字符串。

  5. equals(Object anObject):比较字符串是否相等。

  6. equalsIgnoreCase(String anotherString):比较字符串是否相等,忽略大小写。

  7. compareTo(String anotherString):按字典顺序比较两个字符串。

  8. compareToIgnoreCase(String str):按字典顺序比较两个字符串,忽略大小写。

  9. indexOf(int ch):返回指定字符在字符串中第一次出现的位置。

  10. indexOf(int ch, int fromIndex):返回指定字符在字符串中第一次出现的位置,从指定索引开始搜索。

  11. indexOf(String str):返回指定子字符串在字符串中第一次出现的位置。

Mybatis如何避免 SQL 注入?

MyBatis 是一个基于 Java 的持久层框架,它提供了一种方便的方式来映射数据库表和对象之间的关系。在使用 MyBatis 时,可以通过以下几种方式来避免 SQL 注入:

  1. 使用参数化查询:MyBatis 支持参数化查询,即在查询语句中使用占位符?来代替实际的值。在执行查询时,MyBatis 会将占位符替换为实际的值,并将查询语句传递给数据库执行。这样可以避免将用户输入的字符串直接拼接到查询语句中,从而避免 SQL 注入。

  2. 使用注解:MyBatis 还支持使用注解来指定参数的类型和名称。在使用注解时,MyBatis 会自动将注解中指定的参数值替换为占位符,并将查询语句传递给数据库执行。这样可以避免将用户输入的字符串直接拼接到查询语句中,从而避免 SQL 注入。

  3. 使用预处理语句:MyBatis 还支持使用预处理语句来执行查询。预处理语句是一种将查询语句和参数分开传递给数据库的方式。在执行查询时,数据库会先对查询语句进行解析和优化,然后再将参数值传递给查询语句执行。这样可以避免将用户输入的字符串直接拼接到查询语句中,从而避免 SQL 注入。

  4. 避免使用动态 SQL:动态 SQL 是指在查询语句中使用动态生成的字符串。在使用动态 SQL 时,MyBatis 会将动态生成的字符串直接拼接到查询语句中,从而增加了 SQL 注入的风险。因此,应该尽量避免使用动态 SQL,而是使用参数化查询或注解来指定参数的值。

项目中如何实现跨域?

使用@CrossOrigin注解
  1. 使用 CORS(跨域资源共享):CORS 是一种 W3C 标准,它允许服务器在跨域请求中发送特定的响应头,从而允许客户端访问不同域的资源。在服务器端,可以通过设置响应头来启用 CORS。
  2. 使用 JSONP(JSON with Padding):JSONP 是一种通过动态创建script标签来实现跨域请求的方法。客户端通过向服务器发送一个带有回调函数名称的请求,服务器将响应数据作为参数传递给回调函数。
  3. 使用代理服务器:代理服务器是一种位于客户端和服务器之间的中间服务器,它可以转发客户端的请求到目标服务器,并将响应返回给客户端。通过使用代理服务器,可以实现跨域请求。

@Autowired和@Resource的区别?

  1. @Autowired
    • @Autowired是Spring框架提供的注解,用于自动装配Bean。
    • 它可以用在字段、构造方法、Setter方法上,通过类型来匹配需要注入的Bean。
    • 如果有多个Bean类型匹配,则会按照优先级和Qualifier的指定来确定注入的Bean。
    • @Autowired是按照类型来注入Bean的,如果有多个Bean类型匹配时,可以配合@Qualifier来指定具体要注入的Bean。
  2. @Resource
    • @Resource是JSR-250规范中定义的注解,也用于依赖注入。
    • 它可以用在字段、Setter方法上,通过名称来匹配需要注入的Bean。
    • 默认按照字段名或Setter方法对应的属性名来匹配Bean,也可以通过name属性指定具体要注入的Bean的名称。
    • @Resource是按照名称来注入Bean的,如果没有指定name属性,则默认使用字段名或Setter方法对应的属性名。

两者的功能类似,都用于实现依赖注入,但@Autowired更灵活,可以按照类型来注入Bean,并支持@Qualifier来解决多个Bean类型匹配的问题,而@Resource更简单,直接按照名称来注入Bean。

Springboot的核心注解是哪个?它主要由哪几个注解组成

在 Spring Boot 中,核心注解是@SpringBootApplication,它是一个组合注解,主要由以下几个注解组成:

  1. @EnableAutoConfiguration:该注解用于启用自动配置功能。它会根据项目的类路径和配置文件中的配置,自动配置 Spring Boot 应用程序所需的依赖和配置。
  2. @ComponentScan:该注解用于扫描项目中的组件(例如控制器、服务、Repository 等),并将它们自动注册为 Spring 容器中的 Bean。
  3. @Configuration:该注解用于将当前类标记为配置类。在 Spring Boot 中,配置类是用于配置应用程序的类,可以使用@Bean注解来定义 Bean。
  4. @EnableConfigurationProperties:该注解用于启用配置属性的自动绑定功能。它会根据项目的配置文件中的配置,自动将配置属性绑定到相应的 Bean 中。

深克隆 浅克隆?

  1. 浅克隆

    • 浅克隆是指只复制对象本身,而不复制对象内部的引用类型成员的内容。换句话说,当你克隆一个对象时,它创建了一个新的对象,但是该对象的成员变量仍然指向原始对象中相应的成员变量。
    • 如果原始对象的某个成员变量是一个引用类型(比如另一个对象),那么克隆后的对象中的相应成员变量仍然引用原始对象中的相同对象。这意味着在克隆对象或原始对象中对该成员变量所引用的对象做出修改时,另一个对象也会受到影响。
  2. 深克隆

    • 深克隆是指除了复制对象本身外,还会递归地复制对象内部的引用类型成员的内容。换句话说,当你深克隆一个对象时,它会创建一个新的对象,并且尝试复制对象内部的所有引用类型成员变量,而不是简单地复制引用。
    • 深克隆确保了克隆对象与原始对象之间的彻底隔离,即使对其中一个对象的成员变量做出修改,另一个对象也不会受到影响。

Spring中事务失效的场景有哪些?

  1. 事务方法内未被 Spring 事务管理注解修饰,如@Transactional注解。
  2. 事务方法内发生了异常,但异常被捕获并处理,没有抛出到外层。
  3. 事务方法内执行了非事务性的操作,如手动管理数据库连接或使用了其他框架的事务管理。
  4. 事务传播属性设置不正确,导致事务在嵌套调用中无法正确传播或被嵌套事务覆盖。
  5. 在事务方法内调用了其他方法,但其他方法没有被事务管理注解修饰或没有正确设置事务传播属性。
  6. 使用了全局事务管理,但在事务方法内又使用了局部事务管理,导致事务嵌套错误。
  7. 在事务方法内执行了耗时较长的操作,导致事务超时。
  8. 数据库连接异常或数据库本身的问题导致事务失败。
  9. Spring 配置文件或代码中的事务配置不正确,如事务管理器未正确配置或事务隔离级别设置不合理。

自己总结:

        try catch 异常

        使用public

        使用最终类(final)

MySQL中,如何定位慢查询SQL语句执行很慢, 如何分析呢

  1. 使用慢查询日志:MySQL 提供了慢查询日志来记录执行时间超过指定阈值的查询。你可以通过配置slow_query_log参数来启用慢查询日志,并设置long_query_time参数来指定阈值。然后,你可以使用mysqldumpslow工具来分析慢查询日志。
  2. 使用性能计数器:MySQL 提供了一些性能计数器来监测查询的执行情况。你可以使用SHOW GLOBAL STATUSSHOW STATUS命令来查看性能计数器,并分析查询的执行时间、查询次数等信息。
  3. 使用EXPLAIN语句:EXPLAIN语句可以提供有关查询执行计划的信息,包括索引使用情况、连接类型等。你可以使用EXPLAIN语句来分析查询的执行计划,并找出可能存在的性能问题。
  4. 使用PROFILE语句:PROFILE语句可以提供有关查询执行过程的详细信息,包括每个操作的时间、CPU 开销等。你可以使用PROFILE语句来分析查询的执行过程,并找出可能存在的性能问题。

mysql什么是索引,以及索引的底层数据结构

  1. B树索引(B-tree Index): B树(Balanced Tree)是一种平衡多路搜索树,常用于数据库系统中实现索引。B树索引的特点是平衡性、高度平衡和有序性,它保持了树的平衡性,并且能够快速定位到目标节点。在MySQL中,通常使用B+树来实现索引,因为B+树相对于B树具有更好的查询性能和存储效率。

  2. 哈希索引(Hash Index): 哈希索引使用哈希函数将索引列的值映射到哈希表中的一个位置,通过这个位置来快速定位到对应的数据。哈希索引适用于等值查询(如=操作符),但不适用于范围查询(如<>操作符),并且在某些情况下可能会出现哈希冲突导致性能下降。

  3. 全文索引(Full-text Index): 全文索引是针对文本类型的列(如TEXTVARCHAR)的索引,用于支持全文搜索功能。全文索引可以在文本内容中快速搜索关键词,并返回相关的记录。MySQL中提供了全文索引的支持,基于倒排索引等数据结构来实现。

  4. 空间索引(Spatial Index): 空间索引用于存储和查询具有空间特征的数据,如地理坐标、几何图形等。空间索引可以帮助进行空间范围查询、距离计算等操作,提高空间数据查询的效率。

  5. 组合索引(Composite Index): 组合索引是指将多个列组合起来创建的索引,可以同时索引多个列,支持多列的查询条件。组合索引可以提高查询效率,尤其是针对多列的查询条件或排序需求。

B树和B+树的区别是什么呢?

  1. 节点结构

    • B树的每个节点既存储数据,又存储索引。这意味着每个节点中既包含关键字,也包含对应的数据指针。
    • B+树的非叶子节点只存储索引,不存储实际的数据。所有的数据都存储在叶子节点中。叶子节点之间使用链表连接,形成有序的叶子节点列表。
  2. 叶子节点

    • 在B树中,所有叶子节点均包含数据,且叶子节点之间没有任何顺序。
    • 在B+树中,叶子节点存储了所有的数据,并且叶子节点之间通过指针连接形成一个有序的链表。
  3. 范围查询

    • 在B树中,由于数据分布在各个节点中,因此范围查询(如区间查询)可以在任意层次上进行,效率较高。
    • 在B+树中,由于所有的数据都存储在叶子节点中,范围查询只需要遍历叶子节点即可,效率也很高。
  4. 插入和删除操作

    • 由于B树的非叶子节点也存储数据,插入和删除操作可能需要修改非叶子节点,因此相对复杂一些。
    • 在B+树中,插入和删除操作只需要修改叶子节点,非叶子节点只需要调整索引,因此相对简单。

总的来说,B+树相对于B树来说,更适合用于数据库索引的实现。它能够提供更好的范围查询性能,插入和删除操作相对简单,并且有利于顺序访问,适合于区间查询和范围扫描等数据库操作。因此,大多数数据库系统(包括MySQL)都选择使用B+树来实现索引。

sql的优化的经验,结合项目说sql优化;

  1. 使用索引

    • 在项目中频繁查询的字段上建立索引,可以加快查询速度。但是要避免过多索引,因为索引的维护也会带来额外的开销。
    • 注意使用组合索引来支持多个查询条件,避免使用过多单列索引。
  2. 优化查询语句

    • 尽量避免在查询中使用SELECT *,而是只选择需要的列,减少数据传输量。
    • 使用EXPLAIN语句分析查询执行计划,找出潜在的性能瓶颈。
  3. 避免在 WHERE 子句中使用函数

    • 如果在 WHERE 子句中使用了函数,数据库可能无法使用索引,导致全表扫描。尽量在程序中处理数据,而不是在 SQL 中处理。
  4. 合理使用连接

    • 使用内连接(INNER JOIN)而不是外连接(LEFT JOIN、RIGHT JOIN),避免返回不必要的数据。
    • 对于大表连接,可以考虑使用子查询或者临时表进行优化。
  5. 分页查询优化

    • 对于需要分页的查询,尽量使用 LIMIT 分页而不是 OFFSET 分页,因为 OFFSET 分页会导致数据库扫描和跳过大量记录。
    • 如果需要快速定位到某一页,可以使用游标分页。
  6. 避免频繁的数据操作

    • 避免频繁的更新和删除操作,尽量使用批量操作或者延迟操作。
    • 合理设计数据表结构,避免出现冗余字段和多余的索引。
  7. 定期维护数据库

    • 定期分析数据库性能,检查索引和查询语句,及时进行优化。
    • 对于大型数据库,可以考虑定期重新构建索引、收集统计信息等操作。
  8. 缓存查询结果

    • 对于一些静态数据或者不经常变化的数据,可以考虑将查询结果缓存起来,减少数据库的压力。

深度分页:(用于超大数量时)

  1. 使用游标分页

    • 使用游标(Cursor)进行分页,相比 OFFSET 分页可以避免跳过大量数据,提高性能。
    • 在每次查询时记录上一次查询结果的最后一条数据,作为游标,然后根据游标继续查询下一页的数据。
  2. 使用WHERE子句进行分页

    • 如果能够确定查询结果集的唯一有序字段(如ID、时间戳等),可以使用 WHERE 子句结合比较操作符进行分页,而不是使用 OFFSET。
    • 例如,假设查询结果按照ID升序排序,可以使用WHERE id > last_id ORDER BY id LIMIT page_size进行分页查询。
  3. 使用分页缓存

    • 对于一些静态数据或者不经常变化的数据,可以考虑将查询结果缓存起来,并根据需要进行分页操作。
    • 缓存可以有效减轻数据库的压力,提高深度分页的性能。
  4. 数据预处理

    • 在数据量较大的情况下,可以预先将数据按照某种方式分段或者分块存储,以减少每次查询的数据量。
    • 例如,可以按照时间范围或者其他业务逻辑进行数据划分,然后根据需求进行查询。
  5. 考虑分库分表

    • 如果数据量非常庞大,可以考虑使用分库分表来分散数据存储和查询压力。
    • 将数据按照一定的规则划分到不同的数据库实例或者数据表中,可以提高查询性能和并发能力。

mysql事务的特性是什么以及并发事务有哪些问题

MySQL 事务的特性是:

  1. 原子性(Atomicity):事务中的所有操作要么全部执行,要么全部不执行。
  2. 一致性(Consistency):事务执行后,数据库的状态必须保持一致。
  3. 隔离性(Isolation):多个事务同时执行时,每个事务的操作对其他事务是隔离的,不会互相干扰。
  4. 持久性(Durability):事务执行后,对数据库的修改是持久的,即使系统崩溃或停电,也不会丢失。

并发事务可能会导致以下问题:

  1. 脏读(Dirty Read):一个事务读取了另一个事务未提交的数据,可能会导致数据不一致。
  2. 不可重复读(Non-Repeatable Read):一个事务在两次读取同一个数据时,得到的结果不一致,可能会导致数据不一致。
  3. 幻读(Phantom Read):一个事务在两次读取同一个范围的数据时,得到的结果不一致,可能会导致数据不一致。

为了解决这些问题,MySQL 提供了四种隔离级别:

  1. 读未提交(Read Uncommitted):允许脏读。
  2. 读已提交(Read Committed):禁止脏读,但允许不可重复读和幻读。
  3. 可重复读(Repeatable Read):禁止不可重复读,但允许幻读。
  4. 串行化(Serializable):禁止脏读、不可重复读和幻读。

数据库的乐观锁和悲观锁是什么?怎么实现的?

  1. 悲观锁

    • 悲观锁的核心思想是,在数据被访问或修改时,假设最坏的情况会发生,因此在操作数据之前先加锁,以确保数据操作的独占性。
    • 实现方式包括数据库的行级锁、表级锁或者数据库引擎自带的锁机制。例如,在MySQL中可以使用SELECT ... FOR UPDATE语句在读取数据时将数据行加锁,防止其他事务同时修改该行数据。
  2. 乐观锁

    • 乐观锁的核心思想是,假设并发访问不会导致冲突,因此不会立即加锁,而是在数据更新时检查是否有其他事务对数据进行了修改。
    • 实现方式通常是在数据表中增加一个版本号(或者时间戳)字段,每次更新数据时都会比较版本号,如果版本号与预期不符,则认为数据已经被修改,需要执行相应的处理。
    • 乐观锁通常通过在更新时使用 WHERE 子句中加入版本号的方式来实现。例如,UPDATE table_name SET column1 = value1, version = new_version WHERE id = desired_id AND version = current_version
  3. 自我理解:

               乐观锁:当用户对某个任务进行操作时,另外一个用户对同id的业务进行更改,便会出现有一个人不成功的提示

                悲观锁:确保数据修改过程中不会发生冲突

乐观锁和悲观锁各有优缺点:

  • 悲观锁适用于并发写入比较频繁的场景,它可以确保在数据修改过程中不会发生冲突,但是在高并发环境下可能会导致性能瓶颈,因为它需要频繁地加锁和解锁。

  • 乐观锁适用于并发读取比较频繁的场景,它可以减少锁的竞争,提高系统的并发性能。但是,如果更新冲突发生频率较高,可能会导致较多的重试操作,影响性能。

Redis分布式锁如何实现,控制Redis实现分布式锁有效时长呢

看门狗:

Redis的"看门狗"(kan'm'gWatchdog)是一种用于实现乐观锁的机制,通常与Redis事务一起使用。它主要用于检测在事务执行期间是否有其他客户端对事务监视的键进行了修改,如果有修改则事务会被取消,从而保证事务的原子性。

下面是Watchdog的基本工作原理:

  1. 客户端在执行事务之前使用WATCH命令监视一个或多个键。这些键的值在事务执行期间可能被其他客户端修改。

  2. 如果在执行事务时,任何一个被监视的键的值发生了变化(包括被删除),那么事务就会被取消。

  3. 当事务被取消时,客户端可以选择重新执行事务,或者根据业务逻辑进行其他处理。

redis做为缓存,数据的持久化是怎么做的? (区别是什么)

  1. 快照(Snapshot)持久化

    • 快照持久化是Redis最基本的持久化方式之一,它通过将内存中的数据以快照的形式写入磁盘来实现持久化。
    • Redis会定期将内存中的数据快照写入磁盘中的一个持久化文件(RDB文件)中,这个操作是通过fork一个子进程来完成的。子进程首先会将数据写入临时文件中,然后替换原来的持久化文件,以保证持久化文件的完整性。
    • 快照持久化的优点是简单、高效,并且可以生成一个完整的数据备份。缺点是在发生故障时可能会丢失最后一次快照之后的数据。
  2. 日志(AOF)持久化

    • 日志持久化是一种基于日志的持久化方式,它通过记录每个写操作(如SET、INCR等)到一个追加(Append Only File,AOF)文件中来实现持久化。
    • Redis首先将写操作追加到AOF文件的末尾,然后可以根据需要对AOF文件进行重写(Rewrite),以减少文件大小和提高性能。
    • 日志持久化的优点是可以提供更高的数据安全性,即使在发生故障时也可以尽可能地恢复数据。缺点是相比快照持久化,AOF文件通常会更大一些,而且重写AOF文件可能会对性能产生一定影响。

区别:

  • 快照持久化是周期性地将整个数据集写入磁盘,适用于数据集较小且可以接受较少数据丢失的场景。而日志持久化则是将每个写操作记录到日志中,适用于对数据完整性要求较高的场景。
  • 快照持久化在恢复时可能会有一定的数据丢失,因为它只保存了最后一次快照之后的数据变更,而日志持久化可以提供更可靠的数据恢复保证。
  • 日志持久化相比快照持久化在恢复时可能需要更多的时间,因为需要重新执行AOF文件中的所有写操作。

JVM由哪些部分组成,运行流程是什么?

  1. 类加载器(ClassLoader):负责将类文件加载到内存中,并为类分配内存空间。
  2. 运行时数据区(Runtime Data Areas):包括方法区、堆、栈、程序计数器等,用于存储类的信息、对象实例、方法参数和局部变量等。
  3. 执行引擎(Execution Engine):负责解释和执行字节码指令,包括字节码解释器、即时编译器和垃圾回收器等。
  4. 本地方法接口(Native Interface):用于调用本地方法,即用其他语言编写的代码。

JVM 的运行流程可以分为以下几个步骤:

  1. 类加载:类加载器将类文件加载到内存中,并为类分配内存空间。
  2. 链接:链接过程包括验证、准备和解析三个阶段,用于确保类的正确性和可用性。
  3. 初始化:在类加载完成后,会执行类的初始化方法,对类进行初始化。
  4. 执行:执行引擎解释和执行字节码指令,包括方法调用、变量赋值、控制流转移等操作。
  5. 垃圾回收:垃圾回收器定期对内存中的对象进行回收,释放不再使用的内存空间。

什么是类加载器,类加载器有哪些?

Java 中的类加载器主要有以下几种:

  1. 启动类加载器(Bootstrap ClassLoader):负责加载 Java 核心类库,如java.lang包中的类。
  2. 扩展类加载器(Extension ClassLoader):负责加载 Java 扩展类库,如java.ext包中的类。
  3. 应用程序类加载器(Application ClassLoader):负责加载应用程序中的类,如用户自定义的类。

什么是双亲委派模型?(JVM为什么采用双亲委派机制)

JVM采用双亲委派模型的原因有以下几点:

  1. 保证类的唯一性:当一个类加载器尝试加载一个类时,如果委托给父类加载器加载,那么无论是在父类加载器还是子类加载器中都只会存在一个唯一的类实例,避免了同名类的冲突。

  2. 保证类的安全性:双亲委派模型可以防止恶意代码通过伪装成JDK的核心类库来欺骗程序,因为即使程序员在自定义类加载器中加载了同名的类,但由于双亲委派机制,最终加载的还是JDK核心类库中的类。

  3. 避免类的重复加载:通过委托父类加载器来加载类,可以避免同一个类被多个类加载器加载多次,节省了内存开销。

  4. 方便类加载器的卸载:如果某个类加载器加载了某个类,那么这个类加载器以及它加载的所有类都不能被GC回收,因为有可能其他类加载器还会用到这些类。采用双亲委派模型,可以保证类加载器的层次结构,便于对整个类加载器树进行管理和卸载。

总的来说,双亲委派模型保证了类加载的顺序和唯一性,提高了JVM的安全性和稳定性。

JVM调优(使用了那些工具,内存泄露排查思路)

  1. 选择合适的JVM版本:随着时间的推移,JVM版本不断更新,新版本通常包含了性能改进和bug修复。选择最新的稳定版本能够带来更好的性能和稳定性。

  2. 调整堆内存大小:通过调整-Xmx和-Xms参数来设置堆内存的最大和最小大小,合理分配堆内存可以提高应用程序的性能。通常情况下,堆内存的大小应根据应用程序的负载情况和硬件资源来进行调整。

  3. 选择合适的垃圾收集器:根据应用程序的特性和性能需求,选择合适的垃圾收集器。对于大部分应用程序,G1垃圾收集器通常是一个不错的选择,但在某些情况下,其他垃圾收集器可能更适合,如CMS(Concurrent Mark-Sweep)或Parallel垃圾收集器。

  4. 调整垃圾收集器的参数:通过调整垃圾收集器的参数来优化垃圾收集的性能。例如,可以通过-Xmn参数设置新生代的大小,通过-XX:MaxGCPauseMillis参数设置垃圾收集器的最大停顿时间等。

  5. 设置合适的线程数:根据应用程序的负载情况和硬件资源来设置线程数。过多的线程会增加上下文切换的开销,而过少的线程可能导致资源浪费和性能下降。

  6. 监控和分析JVM性能:使用JVM监控工具如JVisualVM、Java Mission Control等来监控JVM的运行情况,并根据监控数据进行性能分析和优化。

  7. 避免内存泄漏:定期检查应用程序的内存使用情况,及时发现和修复可能导致内存泄漏的问题,避免因内存泄漏而导致的性能下降和系统崩溃。

  8. 优化代码:优化Java代码以减少不必要的内存和CPU消耗,例如避免创建过多的临时对象、避免使用过多的同步等。

内存泄露:

  1. 确认是否存在内存泄漏:首先要确认应用程序是否真的存在内存泄漏问题,可以通过监控内存使用情况、GC日志等手段来确认。如果发现内存占用持续增长,而且GC频率较低,可能存在内存泄漏。

  2. 使用内存分析工具:利用内存分析工具(如Eclipse Memory Analyzer、VisualVM、YourKit等)对应用程序进行内存分析,查看内存堆中的对象分布和引用关系。通过分析内存快照,可以定位到内存泄漏的原因所在。

  3. 查看对象生命周期:分析对象的生命周期,查看是否有对象被意外地持有引用而无法被释放。可能的原因包括静态变量、集合对象持有的引用、监听器未被正确移除等。

  4. 检查代码中的常见问题:检查代码中是否存在常见的内存泄漏问题,例如未关闭的流、未释放的资源、缓存未清理等。特别是在使用底层资源时,务必要确保资源的正确释放。

  5. 分析GC日志:通过分析GC日志来查看GC的行为和频率,以及内存回收的情况。GC日志中通常会包含有关内存分配、对象回收等详细信息,有助于定位内存泄漏的原因。

  6. 逐步排查:根据分析结果逐步排查可能存在内存泄漏的代码段,可以通过添加日志、修改代码逻辑等方式来确认和修复问题。

  7. 重复测试和验证:在确认并修复了潜在的内存泄漏问题后,进行重复测试和验证,确保内存泄漏问题已经解决。

  8. 监控和预防:定期监控应用程序的内存使用情况,及时发现和预防内存泄漏问题的发生。可以利用监控工具来监控内存使用情况,并设置阈值来触发警报。

微服务负载均衡如何实现的

  1. 客户端负载均衡

    • 在客户端实现负载均衡意味着客户端负责选择目标微服务实例来发送请求。
    • 客户端通常会维护一个服务实例列表,并使用负载均衡算法(如轮询、随机、加权轮询等)来选择要调用的微服务实例。
    • 常见的客户端负载均衡库包括Netflix Ribbon等。
  2. 服务端负载均衡

    • 在服务端实现负载均衡意味着负载均衡功能由服务端的负载均衡器或网关来处理。
    • 负载均衡器会接收客户端的请求,然后根据负载情况选择目标微服务实例来处理请求。
    • 常见的服务端负载均衡器包括Nginx、Envoy、HAProxy等。
  3. 服务注册与发现

    • 微服务通常会注册到服务注册中心,并定期向注册中心发送心跳来更新自身的状态。
    • 客户端或负载均衡器通过服务注册中心获取可用的微服务实例列表,并基于这些信息进行负载均衡。
    • 常见的服务注册与发现工具包括Consul、Etcd、Zookeeper等。
  4. 动态负载调整

    • 根据微服务实例的负载情况和性能指标,动态调整负载均衡策略,以确保资源的合理利用和系统的稳定性。
    • 可以基于CPU利用率、内存使用率、响应时间等指标来进行动态负载调整。
  5. 故障检测与容错处理

    • 负载均衡器通常会定期检测微服务实例的可用性,并从服务实例列表中移除不可用的实例。
    • 在某个微服务实例发生故障或不可用时,负载均衡器会自动切换到其他可用的实例,以确保系统的可用性。

MQ如何保证消息不丢失消息的重复消费问题如何解决的

  1. 持久化存储

    • MQ系统通常会将消息存储在持久化的存储介质(如硬盘)上,以确保即使在MQ服务器或者整个系统崩溃时,消息也能够被安全地保存下来,并在系统恢复后重新投递。
    • 持久化存储通常涉及到文件系统、数据库等底层存储机制的支持。
  2. 消息确认机制

    • 生产者在发送消息到MQ时,通常会等待MQ服务器确认消息已经被成功接收并持久化存储。
    • 消费者在消费消息后,会向MQ服务器发送确认,告知MQ消息已经被成功处理。只有在收到消费者的确认后,MQ服务器才会删除消息。
  3. 消息备份和复制

    • 一些高可用的MQ系统会采用消息备份和复制的方式,将消息存储在多个节点上,并保持一致性,以提高消息的可靠性和持久性。
  4. 消息重试机制

    • 当消息发送或者消费失败时,MQ系统通常会提供消息重试机制,自动重新投递消息。
    • 可以通过设置消息的最大重试次数、重试间隔等参数来控制消息重试的行为。
  5. 消息去重

    • 为了解决消息重复消费的问题,MQ系统通常会提供消息去重机制,通过唯一标识符或者消息ID来确保同一条消息不会被重复消费。
    • 消费者在处理消息时,可以记录已经处理过的消息ID,并在接收到重复消息时进行忽略。
  6. 幂等性设计

    • 对于不可避免的重复消息消费问题,可以在消费者端实现幂等性设计,确保同一条消息被处理多次时,产生的效果是一致的。
    • 幂等性设计可以通过在消费者端记录已经处理过的消息ID或者处理结果来实现。

综上所述,MQ系统通过持久化存储、消息确认机制、消息备份和复制、消息重试机制、消息去重和幂等性设计等多种方式来确保消息不丢失,并解决消息的重复消费问题,从而保障系统的可靠性和稳定性。

如果有100万消息堆积在MQ , 如何解决 ?

  1. 增加消费者数量:增加消费者数量可以提高消息处理的速度,从而减少消息堆积。可以根据实际情况增加消费者的数量,以满足业务需求。
  2. 调整消费者并发度:调整消费者并发度可以提高消息处理的效率。可以根据实际情况调整消费者的并发度,以提高消息处理的速度。
  3. 优化消费者代码:优化消费者代码可以提高消息处理的效率。可以通过优化消费者代码,减少消息处理的时间,从而减少消息堆积。
  4. 增加 MQ 容量:增加 MQ 容量可以提高消息存储的能力,从而减少消息堆积。可以根据实际情况增加 MQ 的容量,以满足业务需求。
  5. 清理过期消息:清理过期消息可以释放存储空间,从而减少消息堆积。可以根据实际情况设置消息的过期时间,定期清理过期消息。
  6. 采用分布式架构:采用分布式架构可以将消息处理任务分布到多个节点上,从而提高消息处理的速度和效率。可以根据实际情况采用分布式架构,以满足业务需求。

线程池的核心参数(线程池的执行原理知道嘛)

  1. corePoolSize:线程池的核心线程数量,即在没有任务需要处理时,线程池仍然保留的线程数量。
  2. maximumPoolSize:线程池的最大线程数量,即线程池可以创建的最大线程数量。
  3. keepAliveTime:线程池中的空闲线程在没有任务需要处理时,保持空闲状态的时间
  4. unit:keepAliveTime 的时间单位,可以是毫秒、秒、分钟等。
  5. workQueue:线程池中的任务队列,用于存储等待处理的任务。
  6. threadFactory:线程池创建线程的工厂,用于创建新的线程。

执行原理

线程池的执行原理是:当有任务需要执行时,线程池会从任务队列中取出一个任务,并将其分配给一个空闲的线程执行。如果任务队列中没有任务,线程池会创建一个新的线程来执行任务。当任务执行完毕后,线程会返回线程池,并等待下一个任务的分配。如果线程池中的线程数量超过了 corePoolSize,线程池会将多余的线程销毁,以释放系统资源。如果线程池中的线程数量超过了 maximumPoolSize,线程池会拒绝新的任务,并抛出 RejectedExecutionException 异常。

线程中wait 和 sleep 区别?

  • wait方法是 Object 类的方法,用于让当前线程等待某个条件的发生,而sleep方法是 Thread 类的方法,用于让当前线程暂停一段时间。
  • wait方法会释放当前线程持有的锁,而sleep方法不会释放锁。
  • wait方法会被其他线程唤醒,而sleep方法不会被其他线程唤醒。
  • wait方法通常用于多线程之间的通信和协作,而sleep方法通常用于控制线程的执行时间。

 redis应用场景,在项目中怎么试用的?

Redis 是一个高性能的内存键值数据库,广泛应用于各种场景中,以下是一些常见的应用场景:

  1. 缓存:Redis 可以作为缓存系统,加速应用程序的访问速度。例如,可以将热点数据存储在 Redis 中,以减少对数据库的访问次数。
  2. 消息队列:Redis 可以作为消息队列,实现消息的可靠传递。例如,可以将消息存储在 Redis 中,并使用消费者从 Redis 中获取消息。
  3. 计数器:Redis 可以作为计数器,用于统计各种数据。例如,可以使用 Redis 来统计网站的访问量、用户的点击量等。
  4. 排行榜:Redis 可以作为排行榜,用于展示各种数据的排名。例如,可以使用 Redis 来展示用户的积分排名、商品的销量排名等。
  5. 分布式锁:Redis 可以作为分布式锁,用于实现分布式系统中的同步和互斥操作。例如,可以使用 Redis 来实现分布式系统中的数据一致性。
  6. 地理位置:Redis 可以作为地理位置存储系统,用于存储和查询地理位置信息。例如,可以使用 Redis 来存储和查询用户的位置信息、商家的位置信息等。

在项目中使用 Redis,可以按照以下步骤进行:

  1. 安装 Redis:可以在本地或服务器上安装 Redis。
  2. 配置 Redis:根据项目需求,配置 Redis 的参数,例如端口、密码、数据类型等。
  3. 连接 Redis:使用 Redis 客户端连接 Redis 服务器。
  4. 操作 Redis:使用 Redis 提供的命令操作 Redis 数据库,例如添加、删除、修改、查询等。
  5. 释放资源:使用完 Redis 后,需要释放 Redis 资源,例如关闭 Redis 客户端、释放 Redis 连接等。

java中常见的数据结构,常见的算法?

  1. 数据结构
    • 数组:有序的元素序列,可以存储固定数量的元素。
    • 列表:动态数组,可以存储任意数量的元素,并支持插入、删除和搜索操作。
    • 集合:无序的元素集合,可以存储唯一的元素,并支持插入、删除和搜索操作。
    • 映射:键值对的集合,可以存储任意数量的键值对,并支持插入、删除和查找操作。
    • 栈:一种特殊的线性结构,只能在一端进行插入和删除操作。
    • 队列:一种特殊的线性结构,只能在一端进行插入操作,在另一端进行删除操作。
  2. 算法
    • 排序算法:对一组数据进行排序,常见的排序算法有冒泡排序、插入排序、选择排序、快速排序、归并排序等。
    • 搜索算法:在一组数据中查找特定的元素,常见的搜索算法有顺序搜索、二分搜索等。
    • 图算法:用于处理图结构的数据,常见的图算法有深度优先搜索、广度优先搜索、最短路径算法等。
    • 动态规划算法:通过将问题分解为子问题来解决问题,常见的动态规划算法有背包问题、最长公共子序列问题等。
    • 贪心算法:在每一步选择当前最优的方案,常见的贪心算法有活动安排问题、霍夫曼编码等。

Mysql的存储引擎有那些?他们之间的区别?

MySQL 常见的存储引擎有 InnoDB、MyISAM、MEMORY 等,他们之间的主要区别如下:

  • InnoDB:支持事务,提供行级锁和外键约束,适合处理大量并发事务的场景。
  • MyISAM:不支持事务,提供表级锁和全文索引,适合处理大量查询的场景。
  • MEMORY:将数据存储在内存中,访问速度快,但数据不持久化,适合处理临时数据的场景。

Jvm内存:

1.堆

2.栈

3.本地方法栈

4.方法区

5.程序计数器


Jvm底层原理

  1. 垃圾回收(Garbage Collection):JVM 负责管理内存分配和释放,通过垃圾回收机制自动回收不再使用的对象,以防止内存泄漏。
  2. 类加载(Class Loading):JVM 会将 Java 类文件加载到内存中,并执行类的初始化和解析。
  3. 运行时数据区:JVM 内存被划分为多个区域,如堆(Heap)、栈(Stack)、方法区(Method Area)等,每个区域有其特定的用途。
  4. 字节码执行:JVM 解释和执行 Java 字节码,将其转换为机器可执行的指令。
  5. 即时编译(Just-In-Time Compilation,JIT):JVM 可以在运行时对热点代码进行即时编译,以提高性能。
  6. 多线程支持:JVM 提供了多线程机制,包括线程的创建、同步、调度等。
  7. 内存管理:JVM 对内存进行分配和管理,包括对象的内存布局、垃圾回收算法等。
  8. 性能优化:了解 JVM 的底层原理可以帮助进行性能优化,如调整垃圾回收策略、优化内存使用等。

HashMap

  1. 哈希函数:HashMap使用哈希函数将键转换为哈希值,以便快速定位到对应的存储位置。哈希函数的质量对于哈希表的性能和冲突处理非常重要。

  2. 数据存储:HashMap的键值对存储在一个数组中。哈希值通过哈希函数计算得到,然后通过对数组长度取模运算得到存储位置的索引。

  3. 冲突处理:当多个键的哈希值相同,即发生冲突时,HashMap会使用链表或红黑树来解决冲突。在较小的哈希表中,通常使用链表来存储冲突的键值对;当哈希表的大小达到一定阈值时,会将链表转换为红黑树,以提高查找效率。

  4. 扩容:当哈希表中的元素数量达到一定阈值时,HashMap会进行扩容操作。扩容会重新计算哈希值,并将元素重新分配到更大的数组中。

  5. 性能考虑:HashMap的性能主要取决于哈希函数的质量、冲突处理的效率以及扩容的开销。为了提高性能,可以选择合适的哈希函数、合理设置初始容量和负载因子等参数。

FullGC、MinorGC 和 MajorGC 是垃圾回收机制中的三种类型,用于回收不再使用的内存空间。下面是它们的简要介绍:

1.FullGC:全量垃圾回收(包括新生代和老年代)
    它会回收整个堆空间,包括年轻代和老年代。FullGC 通常会导致应用程序的暂停时间较长,因为它需要遍历整个堆空间来查找和回收不再使用的对象。  它使用的是标记清除算法.    

     这个算法分为两个步骤:    

       •标记(Mark)过程:找到所有的可以访问的对象,做个指定的标记。

       •清除(Swep)过程:遍历堆内存,把未标记的对象进行一个回收。

Full GC的触发条件
    对于 Minor GC,其触发条件非常简单,当 Eden 空间满时,就将触发一次 Minor GC。而 Full GC 则相对复杂,有以下条件

  ①  调用 System.gc()
       只是建议虚拟机执行 Full GC,但是虚拟机不一定真正去执行。不建议使用这种方式,而是让虚拟机管理内存

  ②  老年代空间不足
       老年代空间不足的常见场景为前文所讲的大对象直接进入老年代、长期存活的对象进入老年代等。  为了避免以上原因引起的 Full GC,应当尽量不要创建过大的对象以及数组。除此之外,可以通过 -Xmn 虚拟机参数调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代。还可以通过 -XX:MaxTenuringThreshold 调大对象进入老年代的年龄,让对象在新生代多存活一段时间

  ③  JDK 1.7 及以前的永久代空间不足
       在 JDK 1.7 及以前,HotSpot 虚拟机中的方法区是用永久代实现的,永久代中存放的为一些 Class 的信息、常量、静态变量等数据。   当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,在未配置为采用 CMS GC 的情况下也会执行 Full GC。如果经过 Full GC 仍然回收不了,那么虚拟机会抛出 java.lang.OutOfMemoryError。


 为避免以上原因引起的 Full GC,可采用的方法为增大永久代空间或转为使用 CMS GC

2.MinorGC:小量垃圾回收(只包括新生代)
        它只回收年轻代的内存空间。年轻代是堆空间中最容易被填满的部分,因为它存储的对象通常寿命较短。 MinorGC 通常会比 FullGC 更频繁地发生,因为它可以更快速地回收内存空间,而不会导致应用程序的暂停时间过长。

3.MajorGC:大量垃圾回收(只包括老年代)
    它主要回收老年代的内存空间。老年代是堆空间中存储寿命较长的对象的部分,通常只有在年轻代空间不足时才会触发 MajorGC。 MajorGC 的暂停时间通常比 MinorGC 更长,因为它需要遍历整个老年代空间来查找和回收不再使用的对象。

需要注意的是,FullGC、MinorGC 和 MajorGC 的具体实现和触发条件可能因不同的垃圾回收算法和应用程序的具体情况而有所不同。在实际应用中,需要根据具体情况选择合适的垃圾回收算法和参数,以达到最佳的性能和内存使用效率。

CountDownLatch 是 Java 多线程编程中的一个同步工具类,用于等待一组线程完成执行。

它的主要作用是允许一个线程等待其他线程完成特定任务。

保证多个线程都在执行的时候,一起结束:

CoCountDownLatch 的工作原理如下:tDownLatch 是 Java 多线程编程中的一个同步工具类,用于等待一组线程完成执行。

它的主要作用是允许一个线程等待其他线程完成特定任务。

  1. 在每个需要等待的线程中调用 countDown 方法,表示该线程的任务已经完成。
  2. 在等待的线程中调用 await 方法,会阻塞该线程,直到 countDown 方法被调用的次数达到初始化时指定的数值。

使用 CountDownLatch 的好处包括:

  1. 实现线程间的同步等待。
  2. 可以方便地控制多个线程的执行顺序。
  3. 提高程序的可读性和可维护性。
  1. 在每个需要等待的线程中调用 countDown 方法,表示该线程的任务已经完成。
  2. 在等待的线程中调用 await 方法,会阻塞该线程,直到 countDown 方法被调用的次数达到初始化时指定的数值。

使用 CountDownLatch 的好处包括:

  1. 实现线程间的同步等待。
  2. 可以方便地控制多个线程的执行顺序。
  3. 提高程序的可读性和可维护性。
  • 12
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值