• Java重要知识点


    Java

    JVM

    参考:https://www.kuangstudy.com/bbs/1557549426359590914

    1. 请你谈谈你对jvm的理解?
    2. Java8虚拟机和之前的变化更新?
    3. 什么是OOM?什么是栈溢出StackOverFlowError?怎么分析?
    4. jvm的常见调优参数有哪些?
    5. 内存快照如何抓取?怎么分析Dump文件?
    6. 谈谈jvm中,类加载器你的认识?
    • JVM的位置

    在这里插入图片描述

    • JVM的体系结构

      • jvm结构图,从.java文件 -> .class文件 -> classloader -> 分配空间

        在这里插入图片描述

      • jvm垃圾回收,在jvm中,线程私有的区域不会存在垃圾回收,因为都是栈和程序计数器,如果存在垃圾,那么程序就会死掉,无法正常运行。

      在这里插入图片描述

      • jvm调优,调优肯定是垃圾回收的优化,那么就是线程共享的区域:堆、方法区

        在这里插入图片描述

    类加载器

    • 类加载的过程:加载、初始化、实例化,这里的Car Class可以看作是一个模板

    在这里插入图片描述

    • 哪些类加载器:

      • 引导类加载器(BootstrapClassloader):用C++编写,是JVM自带的类加载器;负责加载Java的核心类库。(该加载器无法直接获取)
      • 扩展类加载器(ExtClassloader):负责加载/jre/lib/ext目录下的jar包。
      • 应用程序类加载器(AppClassloader):负责加载java -classpath或-D java.class.path所指的目录下的类与jar包。(最常用的加载器)
    • 双亲委派机制:(检查顺序从下至上,加载顺序从上至下,也就是从app->ext->boot检查,从boot->ext->app加载)

      • 类加载器接收到一个加载请求时,他会委派给他的父加载器,实际上是去他父加载器的缓存中去查找是否有该类,如果有就加载返回,如果没有则继续委派给父类加载,直到顶层类加载器。
      • 如果顶层类加载器也没有加载该类,则会依次向下查找子加载器的加载路径,如果有就加载返回,如果都没有,则会抛出异常。

    双亲委派机制的类加载流程:

    1、类加载器收到类加载请求

    2、将这个请求向上委托给父类加载器,一直向上委托,直到启动类加载器

    3、启动类加载器检查是否能执行该类,能加载就结束,使用当前加载器,不能加载就抛出异常通知子加载器进行加载。

    如果类加载器返回null,那么java中找不到该类,可能在C、C++中,注意:java中调用系统本地的方法使用native字段,比如线程的线程的启动方法,java处理不了,就只能去用接口调用本地方法。

    • 沙箱安全机制,了解

    Native、方法区

    • Native:

      • 凡是使用了native关键字的,说明Java的作用范围已经达不到了,它会去调用底层的C语言的库。
        • 进入本地方法栈。
        • 调用本地方法接口。JNI(Java Native Interface)的作用:扩展Java的使用,融合不同的语言为Java所用。(最初是为了融合C、C++语言)
        • 所以Java在JVM内存区域专门开辟了一块标记区域Native Method Area Stack,用来登记native方法。
          在最终执行(执行引擎执行)的时候,通过JNI来加载本地方法库中的方法。
    • 方法区:Method Area方法区(此区域属于共享区间,所有定义的方法的信息都保存在该区域)
      方法区是被所有线程共享,所有字段、方法字节码、以及一些特殊方法(如构造函数,接口代码)也在此定义。

    静态变量(static)、常量(final)、类信息(构造方法、接口定义)(class模板)、运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关。

    • 程序计数器:Program Counter Register
      每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向像一条指令的地址,也即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计。

    • 栈的作用:栈内存,主管程序的运行,生命周期和线程同步;线程结束,栈内存也就释放了,对于栈来说,不存在垃圾回收问题

    • 栈存储的内容:8大基本类型、对象引用,实例的方法。

    • 栈的运行原理:本地变量->局部变量表,注意父帧和子帧

    img
    • 栈+堆+方法区之间的交互关系:

    在这里插入图片描述

    • 概念:Heap,一个JVM只有一个堆内存,堆内存的大小是可以调节的。

    • 类加载器读取了类文件后,一般会把什么东西放到堆中?
      类、方法、常量、变量、保存我们所有引用类型的真实对象。

    • 堆内存中细分为三个区域:

      • 新生代内存(Young Generation)(Eden)
      • 老生代(Old Generation)
      • 永久代(Permanent Generation) -> 元空间,可以理解为方法区的具体实现,所以常量池,静态变量,Class放在元空间中
    • 堆内存结构

    在这里插入图片描述

    • 永久代和元空间:方法区是一种规范,不同的虚拟机厂商可以基于规范做出不同的实现,永久代和元空间就是出于不同jdk版本的实现。
      方法区就像是一个接口,永久代与元空间分别是两个不同的实现类。
      只不过永久代是这个接口最初的实现类,后来这个接口一直进行变更,直到最后彻底废弃这个实现类,由新实现类—元空间进行替代。

      • jdk1.8之前:

        在这里插入图片描述

      • jdk1.8:在堆内存中,逻辑上存在,物理上不存在(元空间使用的是本地内存)

        在这里插入图片描述

    • 常量池:

      • 在jdk1.7之前,运行时常量池+字符串常量池是存放在方法区中,HotSpot VM对方法区的实现称为永久代。

        在这里插入图片描述

      • 在jdk1.7中,字符串常量池从方法区移到堆中,运行时常量池保留在方法区中。

        在这里插入图片描述

      • jdk1.8之后,HotSpot移除永久代,使用元空间代替;此时字符串常量池保留在堆中,运行时常量池保留在方法区中,只是实现不一样了,JVM内存变成了直接内存。

      在这里插入图片描述

    GC垃圾回收

    • 垃圾回收的区域

    在这里插入图片描述

    • 如何判断一个常量是废弃常量:假如在字符串常量池中存在字符串 “abc”,如果当前没有任何 String 对象引用该字符串常量的话,就说明常量 “abc” 就是废弃常量,如果这时发生内存回收的话而且有必要的话,“abc” 就会被系统清理出常量池了。

    • 如何判断一个类是无用的类:无用的类需要同时满足下面3个条件

      • Java堆中不存在该类的任何实例
      • 加载该类的ClassLoader已被收回
      • 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
    • GC之引用计数法:给对象中添加一个引用计数器:

      • 每当有一个地方引用它,计数器就加 1;
      • 当引用失效,计数器就减 1;
      • 任何时候计数器为 0 的对象就是不可能再被使用的。

      这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间循环引用的问题。

    在这里插入图片描述

    所谓对象之间的相互引用问题,如下面代码所示:除了对象 objAobjB 相互引用着对方之外,这两个对象之间再无任何引用。但是他们因为互相引用对方,导致它们的引用计数器都不为 0,于是引用计数算法无法通知 GC 回收器回收他们。

    • GC之标记清楚法:标记-清除(Mark-and-Sweep)算法分为“标记(Mark)”和“清除(Sweep)”阶段:首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象。它作为最基础的收集算法,后续的算法都是对其不足进行改进,这个算法会带来两个明显的问题:

      • 效率问题:标记和清除两个过程效率不高

      • 空间问题:标记清楚后会产生大量不连续的空间碎片

        在这里插入图片描述

    • GC之复制算法(主要使用在新生代):为了解决标记-清除算法的效率和内存碎片问题,复制(Copying)收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。虽然改变了空间和效率的问题,但又产生了新问题:

      • 因为将内存分成两半,那么可用内存就变小了
      • 不适合老年代,如果存货对象数量比较大,复制性能就会变差

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    现在的商业虚拟机都采用这种收集算法来回收新生代,但是并不是将新生代划分为大小相等的两块,而是分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 空间和其中一块 Survivor。在回收时,将 Eden 和 Survivor 中还存活着的对象一次性复制到另一块 Survivor 空间上,最后清理 Eden 和使用过的那一块 Survivor。

    HotSpot 虚拟机的 Eden 和 Survivor 的大小比例默认为 8:1,保证了内存的利用率达到 90%。如果每次回收有多于 10% 的对象存活,那么一块 Survivor 空间就不够用了,此时需要依赖于老年代进行分配担保,也就是借用老年代的空间存储放不下的对象。

    • GC之标记整理算法:根据老年代的特点提出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。由于多了整理这一步,因此效率也不高,适合老年代这种垃圾回收频率不是很高的场景。

    在这里插入图片描述

    • GC之分代回收算法:现在的商业虚拟机采用分代收集算法,它根据对象存活周期将内存划分为几块,不同块采用适当的收集算法。

      一般将堆分为新生代和老年代。

      • 新生代使用: 复制算法
      • 老年代使用: 标记 - 清除 或者 标记 - 整理 算法

    垃圾收集器

    前面的收集算法是内存回收的方法论,而垃圾收集器就是这些方法的具体实现,我们能需要根据具体应用场景选择适合自己的垃圾收集器

    • Serial收集器:串行
    • ParNew收集器:Serial+并行

    了解

    为什么需要对垃圾回收进行分区?

    这其实是和对象的存活概率挂钩的,存活时间长的,放在老年代,减少GC(垃圾回收)扫描的概率,新创建出来的对象实例,一般存放在伊甸园,而且不同的区域采用的垃圾回收的算法也是不一样的。

    Young GC的触发机制

    在新生代的Eden区域满了之后就会触发,采用复制算法来 回收新生代的垃圾

    Old GC和Full GC的触发时机

    1、发生Young GC之前进行检查,如果“老年代可用的连续内存空间” < “新生代历次Young GC后升入老年代的对象 总和的平均大小”,说明本次Young GC后可能升入老年代的对象大小,可能超过了老年代当前可用内存空间,此时必须先触发一次Old GC给老年代腾出更多的空间,然后再执行Young GC

    这也就是 ygc之前,触发old gc

    2、执行Young GC之后有一批对象需要放入老年代,此时老年代就是没有足够的内存空间存放这些对象了,此时必 须立即触发一次Old GC

    这也就是ygc之后,触发old gc

    3、老年代内存使用率超过了92%,也要直接触发Old GC,当然这个比例是可以通过参数调整的。

    其实说白了,上述三个条件你概括成一句话,就是老年代空间也不够了,没法放入更多对象了,这个时候务必执行Old GC对老年代进行垃圾回收。

    参考:https://zhuanlan.zhihu.com/p/420273533

    YoungGC频繁是什么原因?

    1、 系统并发量比较大,频繁发生Young GC,但是每次发生Young GC之后存活对象比较多,而Survivor内存配置不合理,放不下Young GC的存活对象,导致Young GC之后存活对象直接存放在老年代,从而导致老年代内存不足触发Full GC。
    解决方式:通过jstat观察GC情况,合理调整新生代内存以及Eden区Survivor区占比。

    FullGC频繁是什么原因?

    1、系统频繁创建的大对象直接分配到老年代,导致老年代内存不足触发Full GC。

    解决方式:其实这种一般是代码编写不恰当导致的,比如有一些导出的功能,直接select * from xxx,全量导出,导致创建大对象,对于这种情况,一般都是优化代码。
    具体步骤:
    (1)通过jstat观察GC情况。
    (2)通过jmap导出堆内存快照,然后在jvisualvm或者MAT进行分析。

    2、系统发生了内存泄露,创建了大量对象,但是无法回收,导致Young GC之后达到15年岁年龄存放到老年代,久之老年代内存不足导致Full GC。
    解决方式:通常也是代码编写不合理导致内存泄露,对于这种情况,一般都是优化代码。
    具体步骤:
    (1)通过jstat观察GC情况。
    (2)通过jmap导出堆内存快照,然后在jvisualvm或者MAT进行分析。

    3、代码存在手动触发Full GC的代码,如System.gc()。

    解决方式: 通过JVM配置关闭手动GC,-XX:+DisableExplicitGC。

    代理

    • 代理模式:**使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。**比如:日志记录、参数校验、事务管理、权限校验
    • 静态代理:在程序运行之前就给目标类编写了其代理类的方法,并对其编译,程序运行的时候能直接读取

    具体代码可以看javaGuide:https://javaguide.cn/java/basis/proxy.html#_2-%E9%9D%99%E6%80%81%E4%BB%A3%E7%90%86

    • 动态代理不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,我们可以直接代理实现类( JDK、CGLIB 动态代理机制)。**从 JVM 角度来说,动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。**说到动态代理,Spring AOP、RPC 框架应该是两个不得不提的,它们的实现都依赖了动态代理。
    • JDK动态代理的实现流程:JDK动态代理只能代理实现了接口的类
      • 定义一个接口及其实现类;
      • 自定义 InvocationHandler 并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;
      • 通过 Proxy.newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h) 方法创建代理对象。

    具体代码可以看javaGuide:https://javaguide.cn/java/basis/proxy.html#_2-%E9%9D%99%E6%80%81%E4%BB%A3%E7%90%86

    • 静态代理、动态代理最大的区别在于动态代理不需要为每个类创建一个代理,使用统一的代理并通过代理工厂的方式实例化代理类,从而实现代理的动态化。

    • CGLIB动态代理机制:CGLIB是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理。很多知名的开源框架都使用到了CGLIB, 例如 Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。

      • 在使用过程中需要自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法。
      public interface MethodInterceptor
      extends Callback{
          // 拦截被代理类中的方法
          //obj : 被代理的对象(需要增强的对象)
          //method : 被拦截的方法(需要增强的方法)
          //args : 方法入参
          //proxy : 用于调用原始方法
          public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 通过 Enhancer类来动态获取被代理类,当代理类调用方法的时候,实际调用的是 MethodInterceptor 中的 intercept 方法
    • JDK 动态代理和 CGLIB 动态代理对比

      • JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。 另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。
      • 就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。

      基础:

    • 多态:可以看成一个向上转形,Animal dog = new Dog(),将dog转形为Animal,不能调用只有Dog类中存在但是Animal类中不存在的方法;如果子类重写了父类中的方法,那么就执行子类的方法。

    • ==和equals():

      • 类没有重写 equals()方法:通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象,使用的默认是 Objectequals()方法。

      • 类重写了 equals()方法:一般我们都重写 equals()方法来比较两个对象中的属性是否相等;若它们的属性相等,则返回 true(即,认为这两个对象相等).

    • 异常捕获过程中:无论是否捕获或处理异常,finally 块里的语句都会被执行。当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。

    • 静态变量可以被类的所有实例共享。无论一个类创建了多少个对象,它们都共享同一份静态变量。

    • 重载就是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理

    • 方法的重写要遵循“两同两小一大”

      • “两同”即方法名相同、形参列表相同;
      • “两小”指的是子类方法返回值类型应比父类方法返回值类型更小或相等,子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等;
      • “一大”指的是子类方法的访问权限应比父类方法的访问权限更大或相等。

    在这里插入图片描述

    • Java中的引用类型就是除了基本数据类型之外的所有类型(如class类型)

    • 基本类型和包装类型的区别?Java 中有 8 种基本数据类型,分别为:byteshortintlongfloatdoublechar、boolean这八种基本类型都有对应的包装类分别为:ByteShortIntegerLongFloatDoubleCharacterBoolean

      • 成员变量包装类型不赋值就是 null ,而基本类型有默认值且不是 null

      • 包装类型可用于泛型,而基本类型不可以。

      • 基本数据类型的局部变量存放在 Java 虚拟机栈中的局部变量表中,基本数据类型的成员变量(未被 static 修饰 )存放在 Java 虚拟机的堆中。包装类型属于对象类型,我们知道几乎所有对象实例都存在于堆中。

      • 相比于对象类型, 基本数据类型占用的空间非常小

    所有整型包装类对象之间值的比较,全部使用 equals 方法比较

    在这里插入图片描述

    • 如何解决浮点数运算的精度丢失问题?BigDecimal 可以实现对浮点数的运算,不会造成精度丢失。通常情况下,大部分需要浮点数精确运算结果的业务场景(比如涉及到钱的场景)都是通过 BigDecimal 来做的。

    • **为什么重写 equals() 时必须重写 hashCode() 方法?**因为两个相等的对象的 hashCode 值必须是相等。也就是说如果 equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法判断是相等的两个对象,hashCode 值却不相等。

      • equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。

      • 两个对象有相同的 hashCode 值,他们也不一定是相等的(哈希碰撞)。

    Java引用类型:

    • 强引用: 最普通的引用 Object o = new Object(),如果没有被引用那么GC就会被回收。
    • 软引用: 垃圾回收器, 内存不够的时候回收 (缓存)。
    • 弱引用: 垃圾回收器看见就会回收 (防止内存泄漏),ThreadLocal中key是弱引用, 其目的就是讲ThreadLocal对象的生命周期和和线程的生命周期解绑. 减少内存使用
    • 虚引用: 垃圾回收器看见二话不说就回收,跟没有一样 (管理堆外内存) DirectByteBuffer -> 应用到NIO Netty

    ThreadLocal

    • 提供线程内的局部变星,不同的线程之间不会相互干扰,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或组件之间一些公共变量传递的复杂度.

    • 特点:

    特点内容
    线程并发在多线程并发场景下
    传递数据我们可以通过ThreadLocal在同一线程,不同组件中传递公共变量
    (保存每个线程的数据,在需要的地方可以直接获取, 避免参数直接传递带来的代码耦合问题)
    线程隔离每个线程的变量都是独立的, 不会互相影响.(核心)
    (各线程之间的数据相互隔离却又具备并发性,避免同步方式带来的性能损失)
    • ThreadLocal 和Synchronized的区别
    synchronizedsynchronizedThreadLocal
    原理同步机制采用以时间换空间的方式,只提供了一份变量, 让不同的线程排队访问ThreadLocal采用以空间换时间的方式, 为每一个线程都提供了一份变量的副本, 从而实现同访问而相不干扰
    侧重点多个线程之间访问资源的同步多线程中让每个线程之间的数据相互隔离
    • 实现流程

      1. 每个THreadLocal线程内部都有一个Map(ThreadLocalMap)

      2. Map里面存储的ThreadLocal对象(key)和线程变量副本(Value)也就是存储的值

      3. Thread内部的Map是由ThreadLocal维护的, 有THreadLocal负责向map获取和设置线程变量值

      4. 对于不同的线程, 每次获取value(也就是副本值),别的线程并不能获取当前线程的副本值, 形成了副本的隔离,互不干扰.

    • 强弱引用和内存泄漏

      • 如果key是强引用:在这里插入图片描述

      • 如果key是弱引用:在这里插入图片描述

      • 内存泄漏的真实原因:在这里插入图片描述

      • 那么为什么 key 要用弱引用呢:事实上,在 ThreadLocalMap 中的set/getEntry 方法中,会对key为null(也即是ThreadLocal为null)进行判断,如果为null的话,那么将value置为null的.这就意味着使用完 ThreadLocal , CurrentThread 依然运行的前提下.就算忘记调用 remove 方法,弱引用比强引用可以多一层保障:弱引用的 ThreadLocal 会被回收.对应value在下一次 ThreadLocaIMap 调用 set/get/remove 中的任一方法的时候会被清除,从而避免内存泄漏.

    此处参考:https://blog.csdn.net/bbscz007/article/details/105686382

    • 为什么value要强引用呢?假设 key 所引用的 ThreadLocal 对象还被其他的引用对象强引用着,那么这个 ThreadLocal 对象就不会被 GC 回收,但如果 value 是弱引用且不被其他引用对象引用着,那 GC 的时候就被回收掉了,那线程通过 ThreadLocal 来获取 value 的时候就会获得 null,显然这不是我们希望的结果。因为对我们来说,value 才是我们想要保存的数据,ThreadLcoal 只是用来关联 value 的,如果 value 都没了,还要 ThreadLocal 干嘛呢?所以 value 不能是弱引用。

    常见面试题:

    底层原理
    内存泄漏的场景
    为什么虚引用了还要remove
    父线程的ThreadLocal子线程可以用吗
    IheritableThreadLocal原理
    线程池里的线程能用IheritableThreadLocal吗
    为什么ThreadLocalMap中的key要用弱引用,Java有哪些引用
    使用弱引用的话,如果key被垃圾回收了,value怎么办
    那你如果调用get()方法之后,key被回收了,value会不会被回收(猜的)
    threadlocal,定义在哪里的?threadlocal存在哪里?threadlocalMap放在哪里,key和value指向的是啥?
    threadLocal的key就是ThreadLocal的引用,而value就是当前线程在key所对应的ThreadLocal中存储的值。

    线程池

    线程池的状态

    线程池的状态可以通过ThreadPoolExecutor类的getPoolSize()方法,getActiveCount()方法,getCompletedTaskCount()方法和getTaskCount()方法来查询。线程池的状态有以下几种:

    1. Running(运行状态):线程池新建或调用execute()方法后,处于运行状态,能够接收新的任务。

    2. Shutdown(关闭状态):线程池调用shutdown()方法后,线程池的状态会变为Shutdown。此时线程池不再接收新的任务,但会执行已提交的等待任务队列中的任务。

    3. Stop(停止状态):人为调用shutdownNow()方法后,线程池的状态会变为Stop。此时线程池不再接收新的任务,并且会中断正在处理中的任务。

    4. Tidying(整理状态):当线程池处于Shutdown或Stop状态时,如果等待队列中还有未执行的任务,则线程池将执行整理操作,将等待队列中的未执行任务移除,并保存到一个列表中。

    5. Terminated(终止状态):当线程池处于Shutdown状态,并且等待队列中的任务全部执行完毕,或者在Stop状态下,线程池内部的所有线程都已经终止时,线程池进入Terminated状态。

    线程池的submit和execute有什么区别

    1. execute()只能执行实现Runnable接口类型的任务;而submit()不仅可以执实现Runnable类型接口的任务,也可以执行实现Callable接口类型的任务,因此可以有返回值。

    2. 如果线程执行发生异常,submit可以通过Future.get()方法抛出异常,方便我们自定义异常处理;而execute()会终止异常,没有返回值

    3. 队列策略 线程池通常会使用队列来保存已提交但未处理的任务。线程池如何使用这个队列,被称为队列策略。通过submit()方法提交的任务,会被添加到阻塞队列中,并保留之前提交的任务执行顺序。而对于execute()方法提交的任务,将会被添加到队列的尾部。这意味着队列中的第一个任务是最早的任务并且先被执行。

    4. submit()方法在处理任务时,将任务交由一个线程池中的工作线程去处理,而另一个线程(可能是主线程)可以继续做其他事情。负责处理submit()方法提交的任务的线程,当任务结束后会自动返回给线程池并等待下一个任务,从而避免了重复创建和销毁线程的开销。而execute()方法则不同,它的任务直接在调用execute()方法的调用线程(通常是主线程)中运行,如果当前没有可用线程,则会立即创建新的线程来处理该任务,并在完成任务后销毁线程。

    ArrayList扩容机制

    1.ArrayList的扩容是懒惰的,在没有添加元素之前,即使指定了容量,也不会真正创建数组;

    2.使用add方法时:当存入数据时进行扩容,第一次扩容到长度为10的数组,替换掉长度为0的数组;第二次扩容为上一次的1.5倍(实际上是[>>>1]),也就意味着第二次扩容大小为15,同时新数组依旧替换掉旧数组。

    3使用addAll()方法时:

    3.1当ArrayList中为空时,添加的元素的数量小于等于10时,第一次扩容为10,当添加的元素数量大于10时,扩容大小为元素数量。

    例如:添加5个元素,第一次扩容为10;

    添加12个元素,第一次扩容为12。

    3.2当ArrayList中不为空时,规则类似于上一条,添加的元素数量与原本的元素数量如果大于当前容量且小于下次扩容的大小,则为1.5倍数组大小扩容,如果两者之和大于下次扩容大小,则扩容大小为两者元素和大小;

    例如:当前ArrayList中已经有10个元素,再增加3个元素,则一共13个元素,小于扩容大小15,下一次扩容大小为15;

    当前ArrayList中已经有10个元素,再增加6个元素,则一共16个元素,大于扩容大小15,下一次扩容大小为16。

    CyclicBarrier 和 CountDownLatch 的区别

    1、CountDownLatch 简单的说就是一个线程等待,直到他所等待的其他线程都执

    行完成并且调用 countDown()方法发出通知后,当前线程才可以继续执行。

    2、cyclicBarrier 是所有线程都进行等待,直到所有线程都准备好进入 await()方

    法之后,所有线程同时开始执行!

    3、CountDownLatch 的计数器只能使用一次。而 CyclicBarrier 的计数器可以使

    用 reset() 方法重置。所以 CyclicBarrier 能处理更为复杂的业务场景,比如如果

    计算发生错误,可以重置计数器,并让线程们重新执行一次。

    4、CyclicBarrier 还提供其他有用的方法,比如 getNumberWaiting 方法可以获

    得 CyclicBarrier 阻塞的线程数量。isBroken 方法用来知道阻塞的线程是否被中断。

    如果被中断返回 true,否则返回 false。

    说一下Select、Poll、Epoll的区别

    https://zhuanlan.zhihu.com/p/367591714

    在这里插入图片描述

    OOM如何分析

    https://blog.csdn.net/CSDN_WYL2016/article/details/107749678

    算法

    sort函数

    sort()方法会接受一个比较函数compare(a, b),该函数要比较两个值,然后返回一个用于说明这两个值的相对顺序的数字。最后,永远要记住一句话,凡是返回1或者大于0的正数的时候就要交换位置。

    (1)Arrays.sort:针对数组
    递增排序:Arrays.sort(arr);
    递减排序:Arrays.sort(arr, (a, b) -> b - a);
    或使用:Arrays.sort(scores,Collections.reverseOrder()); 需要注意的是 arr不能使用基本类型(int,double, char),如果是int型需要改成Integer,float要改成Float。这里也要注意object[] arr是需要自己new每个对象的,因为object的默认值是null;而int[] arr等基本类型的默认值不是null,所以可以直接使用。

    按[0]维升序排,如果[0]相等,则按[1]维升序排:

    int[][] array = {{5, 7}, {5, 2}, {2, 5}, {5, 8}, {7, 1}, {4, 3}, {2, 3}};
    Arrays.sort(array, (e1, e2) -> e1[0] == e2[0] ? e1[1] - e2[1] : e1[0] - e2[0]);
    
    • 1
    • 2

    (2)Collections.sort:针对集合类(容器)
    递增排序:Collections.sort(list);
    按getName()结果递增排序:Collections.sort(list, (e1, e2) -> e1.getName()-e2.getName());
    按x升序排,如果x相等,则按y升序排:

    Collections.sort(list,(e1,e2)->e1.x==e2.x ? e1.y-e2.y: e1.x-e2.x);
    
    • 1

    (3)类实现Comparable接口,重写compareTo() 比直接在sort()里面用lamda函数时间复杂度低

    class Node implements Comparable{
        int x;
        int y;
        public Node(int x,int y) {
            this.x = x;
            this.y = y;
        }
        @Override
        public int compareTo(Object o) {
            Node node = (Node) o;
            //按x升序排,如果x相等,则按y升序排
            return this.x==node.x?this.y-node.y:this.x-node.x;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    String#equals() 和 Object#equals() 有何区别?

    String 中的 equals 方法是被重写过的,比较的是 String 字符串的值是否相等。 Objectequals 方法是比较的对象的内存地址。

  • 相关阅读:
    Java 八股文能不背吗?Java 面试都只是背答案吗?
    普通人下半年做电商还有机会吗?
    清朝盛衰的六个时间点!
    Shaderlab的组成部分SubShader
    参照DefenseGrid在Unity中实现合理的塔防寻路机制
    关于激光探测器光斑质心算法在FPGA硬件的设计
    MySQL8--Windows下使用压缩包安装的方法
    机器学习(六)支持向量机SVM
    #力扣:9. 回文数@FDDLC
    (附源码)计算机毕业设计SSM基于的高校在线办公系统
  • 原文地址:https://blog.csdn.net/qq_41291067/article/details/132757824