面试专题一

Java并发篇

相关演示代码地址:guyuexuan039/interview: 面试知识点汇总 (github.com)

一、Java如何开启线程?怎么保证线程安全?

首先说一下线程和进程的区别:进程是操作系统进行资源分配的最小单元;线程是操作系统进行任务分配的最小单元。线程隶属于进程。 如何开启线程? 1.继承 Thread类,重写run方法。 2.实现Runnable接口,实现run方法。 延伸到start方法和run方法的区别,调用start方法开启一个线程,而在start方法内部呢会调用run方法去实现一个线程的逻辑。而如果是我们主观地直接调用run方法,那就是一个普通的方法调用,并不会开启线程。 3.实现Callable接口,实现 call 方法。传统的Runnable和Thread的run方法是没有返回值的,如果你想返回一下线程的执行结果,就需要通过 Callable接口来实现。通过 FutureTask创建一个线程,获取到线程执行的返回值。 4.通过线程池来开启线程(不熟悉线程池的话就不要提这种方式) 怎么保证线程安全? 什么是线程安全:多个资源对同一个线程进行操作就会出现线程安全的问题。 加锁: 1. JVM提供的锁,也就是Synchronized关键字 2. JDK提供的各种锁 Lock 注意:既然你回答到了上面的锁,那面试官就会顺其自然地问你你了解哪些锁?所以,自己需要补充这块知识。 锁分类:公平锁和非公平锁 公平锁:是指多个线程按照申请锁的顺序来获取锁 非公平锁:是指多个线程获取的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。 ReentrantLock:可以指定构造方法的boolean类型来指定是公平锁还是非公平锁,默认是非公平锁 synchronized:是一种非公平锁。 可重入锁(又名递归锁):指的是同一线程外层方法获得锁之后,内层递归方法仍然能够获得该锁的代码 示例如下: class MyClass { ​ public synchronized void method1() { ​ method2(); ​ } ​ public synchronized void method2() { ​ } } method1和method2都是synchronized修饰的方法,在method1里面调用method2的时候,不需要重新申请锁,可以直接调用就行了(其实可以反过来想一想,如果synchronized不具有重入性,当我调用了method1的时候,得申请锁,申请好了之后那么method1就拥有了这个锁,那么调用method2的时候,又要重新申请锁,而锁在method1的手上,这时候又要重新申请锁,显然是不可能得到的,这不科学。所以,synchronize和lock都是具有可重入性的) 具体锁的内容可以参考:https://zhuanlan.zhihu.com/p/85511613

二、Volatile和Synchronized有什么区别?Volatile能不能保证线程安全?DCL(Double Check Lock)单例为什么要加 Volatile?

1. Synchronized关键字,用来加锁。Volatile只是保持变量的线程可见性。通常适用于一个线程写,多个线程读的场景。 2. Volatile不能保证线程安全。Volatile关键字只能保证线程可见性,不能保证原子性。 3. Volatile防止指令重排。在DCL中,防止高并发情况下,指令重排造成的线程安全问题。

三、Java线程锁机制是怎样的?偏向锁、轻量级锁、重量级锁有什么区别?锁机制是如何升级的?

1. Java的锁就是在对象的MarkWord中记录一个锁状态。无锁、偏向锁、轻量级锁、重量级锁对应不同的锁状态。 2. Java的锁机制就是根据资源竞争的激烈程度不断进行锁升级的过程。

四、谈谈你对AQS的理解。AQS如何实现可重入锁?

1. AQS(AbstractQueuedSynchronizer)是一个java线程同步的框架。是jdk中很多锁工具的核心实现框架。 2. 在AQS中,维护了一个信号量state和一个线程组成的双向链表队列。其中,这个线程队列就是用来给线程排队的,而state就像是一个红绿灯,用来控制线程排队或者放行的。在不同的场景下,有不同的意义。 3. 在可重入锁场景下,state就用来表示加锁的次数。0表示无锁,每加一次锁,state就加1.释放锁state就减1。

五、有A,B,C三个线程,如何保证三个线程同时执行?如何在并发情况下保证三个线程依次执行?如何保证三个线程有序交错运行?

当面试官提到这个问题时,一定要想到其考察的是以下三个并发工具:CountDownLatch, CylicBarrier, Semaphore CountDownLatch就像是百米赛跑,由指挥官统一发射开始信号,然后多个线程同时开始执行。 CylicBarrier就像是做核酸检测时一样,一组10人,当一个人来了后,先等待,只有当够了10人后,才会进行统一检测;也就说它保证了多个线程的有序依次执行。 相关代码参看:threadSafe.ThreadSafeDemo

六、如何对一个字符串快速进行排序?

首先如果是面试官在并发篇中间插入这个问题,那么面试官很可能问你的是你对 Fork/Join 框架的理解,不是传统的冒泡,快排,交换等排序算法 基本模式是 拆分,汇总 参看代码:forkjoin.MergeTest

Java网络通信篇

一、TCP和UDP有什么区别?TCP为什么是三次握手,而不是两次?

TCP Transfer Control Protocol是一种面向连接的、可靠的、传输层通信协议。 特点:好比是打电话,面向连接的,点对点的通信,高可靠的,效率比较低,占用的系统资源比较多。 UDP User Datagram Protocol 是一种无连接的、不可靠的、传输层通信协议。 特点:好比是广播,不需要连接,发送方不管接收方有没有准备好,直接发消息;可以进行广播发送的;传输不可靠,有可能丢失消息;效率比较高,占用的系统资源便较少。 TCP建立连接三次握手,断开连接四次挥手。主要从网络的不稳定性方面考虑,网络的不稳定性是确定的,但是我们必须保证会话的准确性,因此至少需要三次握手来保证会话的可靠性。 如果是两次握手,可能造成连接资源浪费的情况。

二、Java有哪几种IO模型?有什么区别?

BIO 同步阻塞IO,传统型 web 服务就是这种模型:

特点:可靠性差,吞吐量低,适用于连接比较少且比较固定的场景。JDK1.4之前唯一的选择。

NIO 同步非阻塞IO

在 BIO 的基础上,增加一个 selector ,减少 Thread 对 Server 的压力,就是 NIO:

特点:可靠性比较好,吞吐量较高,适用于连接比较多并且连接比较短(因为对于客户端来说还是一个轮询,查看是否服务端响应了没),适用于轻操作,例如聊天室

AIO 异步非阻塞IO

在 NIO 的基础上,让 Server 处理完成后,主动推送响应到 客户端就是 AIO 了:

特点:可靠性是最好的,吞吐量也是非常高。适用于连接比较多,并且连接也比较长(重操作)。例如:相册服务器。JDK7版本才开始支持

同步、异步和阻塞、非阻塞:同步,异步针对的是请求;阻塞,非阻塞针对的是响应。 在一个网络请求中,客户端会发一个请求到服务端 1. 客户端发了请求后,就一直等着服务端响应。客户端:阻塞。请求:同步 2. 客户端发了请求后,就去干别的事情了。时不时的过来检查服务端是否给出了响应。客户端:非阻塞。请求:同步。 3. 换成异步请求。客户端发了请求后,就坐在椅子上,等着服务端返回响应。客户端:阻塞。请求:异步。 4. 客户端发了请求后,就去干别的事情了。等到服务端给出响应后,再过来处理业务逻辑。客户端:非阻塞。请求:异步。 具体内容参考文章:https://blog.csdn.net/qq_42216791/article/details/107316926

三、Java NIO的几个核心组件是什么?分别有什么用?

三大组件: Channel Buffer Selector Channel类似于流,每个channel对应一个buffer缓冲区。channel会注册到selector上。 selector会根据channel上发生的读写事件,将请求交由某个空闲的线程去处理。selector对应一个或多个线程。 buffer和channel都是可读可写的。

四、select, poll 和 epoll 有什么区别?

这个问题就很深了,与操作系统相关。 这三个东西呢,其实就是上面的核心组件Selector在操作系统中的具体实现机制。 他们是NIO中多路复用的三种实现机制,是由Linux操作系统提供的。 用户空间和内核空间:操作系统为了保护系统安全,将内核划分为两个部分,一个是用户空间,一个是内核空间。用户空间能直接访问底层的硬件设备,必须通过内核空间。 文件描述符File Descriptor(FD):是一个抽象的概念,形式上是一个整数,实际上是一个索引值。指向内核中为每个进程维护进程所打开的文件的记录表。当程序打开一个文件或者创建一个文件时,内核就会向进程返回一个FD。只存在于 Unix,Linux中。 select机制:会维护一个FD的集合fd_set。将fd_set从用户空间复制到内核空间,激活socket。 poll机制:和select机制是差不多的,把fd_set结构进行了优化,fd集合的大小就突破了操作系统的限制。 epoll:event poll。epoll不再扫描所有的FD,只将用户关心的FD的事件存放到内核的一个事件表当中。这样,可以减少用户空间和内核空间之间需要拷贝的数据。

五、描述下 Http 和 Https 的区别

HTTP:是互联网上应用最为广泛的一种网络通信协议,基于TCP,可以使浏览器工作更为高效,减少网络传输。 HTTPS:是http的加强版,可以认为是http+ssl(secure socket layer)。在http的基础上增加了一些列的安全机制。一方面保证数据传输安全,另一方面对访问者增加了验证机制。是目前现行架构下,最为安全的解决方案。 主要区别: 1. HTTP的连接是简单无状态的,HTTPS的数据传输是经过证书加密的,安全性更高。 2. HTTP是免费的,而HTTPS需要申请证书,而证书通常是需要收费的,并且费用不低。 3. 他们的传输协议不同,所以他们使用的端口也是不一样的,HTTP默认是80端口,而HTTPS默认是443端口。 HTTPS的缺点: 1. HTTPS的握手协议比较费时,所以会影响服务的响应速度以及吞吐量。 2. HTTPS也并不是完全安全的。它的证书体系其实也并不是完全安全的。并且HTTPS在面对DDOS这样的攻击时,几乎起不到任何作用。

JVM调优篇

一、说一说JVM的内存模型?

二、Java类加载的全过程是怎样的?什么是双亲委派机制?有什么作用?一个对象从加载到JVM,再到被GC清除,都经历了什么过程?

Java的类加载器:AppClassLoader -> ExtClassLoader -> BootstrapClassLoader 每种类加载器都有他自己的加载目录。 Java中的类加载器:AppClassLoader,ExtClassLoader -> URLClassLoader -> SecureClassLoader -> ClassLoader 每个类加载器对它加载过的类,都是有一个缓存的。 双亲委派:向上委托查找,向下委托加载。作用:保护了Java底层的类不会被应用程序覆盖。(例如:String,List等类不会被你自定义的同名String等给覆盖掉) 类加载过程:加载 -> 连接 -> 初始化 加载:把Java的字节码文件加载到JVM内存当中,并映射成JVM认可的数据结构。 连接:分为三个小的阶段:1. 验证:检查加载到的字节信息是否符合JVM规范;2. 准备:创建类或接口的静态变量,并赋初始值,半初始化状态;3.解析:把符号引用转为直接引用。 初始化: 1. 用户创建一个对象,JVM首先需要到方法区去找对象的类型信息,然后再创建对象。 2. JVM要实例化一个对象,首先要在堆当中先创建一个对象。 -> 半初始化状态 3. 对象首先会分配在堆内存中新生代的Eden。然后经过一次Minor GC,对象如果存活,就会进入S区。在后续的每次GC中,如果对象一直存活,就会在S区来回拷贝,每移动一次,年龄加1. -> 多大年龄才会移入老年代?年龄是4个bit,最大为15,超过一定年龄后,对象转入老年代。 4. 当方法执行结束后,栈中的指针会先移除掉。 5. 堆中的对象,经过Full GC,就会被标记为垃圾,然后被GC线程清理掉。

三、怎么确定一个对象到底是不是垃圾?什么是GC Root?

有两种定位垃圾的方式: 1. 引用计数:这种方式是给堆内存当中的每个对象记录一个引用计数。引用个数为0的就认为是垃圾。这是早期JDK中使用的方式。引用计数无法解决循环引用的问题。 2. 根可达算法:这种方式是在内存中,从引用根对象向下一直找引用,找不到的对象就是垃圾。 哪些是GC Root? stack -> JVM stack, Native stack, class类, run-time constant pool常量池,static reference 静态变量

四、JVM有哪些垃圾回收算法?

MarkSweep 标记清除算法

这个算法分为两个阶段,标记阶段:把垃圾内存标记出来,清除阶段:直接将垃圾内存回收。

这种算法是比较简单的,但是有个很严重的问题,会产生很多内存碎片。

Copying 拷贝算法

为了解决标记清除算法的内存碎片问题,就产生了拷贝算法。

拷贝算法将内存分为大小相等的两半,每次只使用其中一半。垃圾回收时,将当前这一块的存活对象全部拷贝到另一半,然后当前这一半内存就可以直接清除。

这种算法没有内存碎片,但是它的问题就在于浪费空间。而且,它的效率跟存活对象的个数有关。

MarkCompack 标记压缩算法

为了解决拷贝算法的空间利用缺陷,就提出了标记压缩算法。这种算法在标记阶段跟标记清除算法是一样的,但是在完成标记后,不是直接清理垃圾内存,而是将存活对象往一端移动,然后将端边界以外的所有内存直接清除。

这三种算法各有利弊,各自有各自的适合场景。

五、JVM有哪些垃圾回收器?他们都是怎么工作的?什么是STW?它都发生在哪些阶段?什么是三色标记?如何解决错标记和漏标记的问题?为什么要设计这么多的垃圾回收器?

STW:Stop-The-World,是在垃圾回收算法执行过程当中,需要将JVM内存冻结的一种状态。

在STW状态下,Java的所有线程都是停止执行的-GC线程除外,native方法可以执行,但是,不能与JVM交互。GC各种算法优化的重点,就是减少STW,同时这也是JVM调优的重点。

六、 如何进行JVM调优?JVM参数有哪些?怎么查看一个Java进程的JVM参数?谈谈你了解的JVM参数。如果一个Java程序每次运行一段时间后,就变得非常卡顿,你准备如何对他进行优化?

posted @ 2022-06-21 23:51  LoremMoon  阅读(30)  评论(0编辑  收藏  举报