Roy
自媒体
·2023-08-19
#面试# 快手面经 操作系统: 1.线程和进程有什么关系,有什么区别? 定义:进程是一个正在执行的程序实例,具有独立的内存空间和资源。线程是进程的一部分,用于执行进程内的具体任务。 资源占用:每个进程都有自己独立的内存空间和资源,包括文件、网络线程、内核数据结构等。线程共享进程的资源,包括内存空间、文件描述符等。 调度和执行:进程在操作系统中被调度为可执行状态,并并行或交替执行。线程在进程内被调度执行,共享进程的执行环境。 交互和通信:进程之间通常通过进程间通信(IPC)进行交互和通信,如管道、消息队列、共享内存等。线程之间由于共享相同的内存空间,可以直接访问和修改共享变量,实现线程间的通信和同步。 异常和崩溃:进程之间具有强隔离性,一个进程的崩溃不会影响其他进程。线程共享同一个进程的资源,一个线程的崩溃可能会影响整个进程的稳定性。 创建和销毁:创建和销毁进程需要操作系统的调用,涉及加载和卸载程序、分配和释放内存等操作。线程的创建和销毁更轻量级,通常由程序代码直接调用线程库函数进行操作。 2.什么是虚页? 在现代计算机系统中,使用了虚拟内存技术来更好地管理和利用系统的内存资源。虚拟内存通过将物理内存和磁盘空间结合起来,为每个进程提供一个逻辑上连续的地址空间,称为虚拟地址空间。 虚拟页和物理页(也称为页框)之间存在映射关系。操作系统利用页表(Page Table)来维护虚拟页与物理页之间的映射关系。页表的每个表项记录了一个虚拟页与一个物理页之间的映射关系。 当程序访问虚拟地址空间中的某个虚拟页时,操作系统首先在页表中查找对应的物理页。如果映射存在,操作系统将虚拟页映射到物理页并将该页加载到主存中,然后将虚拟地址转换为物理地址,程序就可以在主存中访问该虚拟页所对应的物理页了。 Java基础: 1.Map的实现类有哪些,介绍一下它们: HashMap:HashMap是最常用的Map实现类之一。它基于哈希表实现,可以提供快速的插入、删除和查找操作。HashMap允许存储键值对,其中键是唯一的,值可以重复。它不保证元素的顺序,也不是线程安全的。 TreeMap:TreeMap是基于红黑树实现的有序Map。它会根据键的自然顺序或者自定义比较器对键进行排序。TreeMap的插入、删除和查找操作的时间复杂度为O(logN)。由于它对键进行排序,因此遍历时会按照键的顺序输出。TreeMap不是线程安全的。 LinkedHashMap:LinkedHashMap是HashMap的子类,它保持了插入顺序或者访问顺序(通过构造函数指定)。LinkedHashMap使用哈希表和双向链表实现,因此可以在常数时间内维护键的顺序。LinkedHashMap也不是线程安全的。 ConcurrentHashMap:ConcurrentHashMap是线程安全的HashMap实现。它通过将整个Map分成多个段(Segment)来提高并发访问的性能。这样,在大多数情况下,不同的线程可以同时访问Map,从而提高性能。ConcurrentHashMap不保证元素的顺序。 Hashtable:Hashtable是早期的哈希表实现,它是线程安全的。虽然Hashtable的使用已经不那么常见,但由于它是线程安全的,因此在多线程环境中仍然可以使用。 2.HashMap的应用场景是什么? HashMap是一种常用的Map实现类,适用于以下场景: 快速查找:由于HashMap基于哈希表实现,可以快速进行查找操作。如果需要根据键快速查找对应的值,HashMap是一个很好的选择。 键值唯一:HashMap的键是唯一的,不允许重复。这对于需要存储唯一键值对的场景非常适用,例如存储用户ID和对应的用户信息。 无需排序:HashMap不保证元素的顺序,插入和删除操作的顺序也不会影响其内部结构。如果不需要对键进行排序,只需要快速存储和查找键值对,那么HashMap是一个很好的选择。 非线程安全环境:HashMap不是线程安全的,适用于在单线程环境下使用。如果需要在多线程环境中使用Map,可以考虑使用ConcurrentHashMap。 Java并发编程: 1.为什么要用线程池? - 降低线程创建和销毁的开销:线程的创建和销毁是相对昂贵的操作,涉及到分配内存、初始化上下文等耗时操作。如果每次需要执行任务时都创建一个新的线程,会造成大量的系统资源消耗和额外的开销。线程池通过预先创建一组可复用的线程,避免了频繁的线程创建和销毁操作,降低了系统的开销。 - 控制并发度和资源消耗,避免系统崩溃:线程池可以控制并发执行的任务数量,通过限制线程池的大小和配置线程的数量,可以合理地控制并发度,避免过多的任务同时执行导致系统资源耗尽,系统崩溃。同时,线程池可以对任务进行排队和调度,合理利用系统资源,避免资源的浪费。 - 提供线程管理和监控功能:线程池可以对线程进行统一的管理和调度,包括线程的创建、销毁、复用、暂停、恢复等操作。线程池还可以监控线程的状态、执行情况和资源使用情况等,提供了对线程池的可控和可视化管理。 2.怎么创建线程池? 创建一个ThreadPoolExecutor对象,可以使用ThreadPoolExecutor的构造方法或者Executors类提供的工厂方法来创建。 具体: 创建一个ThreadPoolExecutor对象,可以使用ThreadPoolExecutor的构造方法或者Executors类提供的工厂方法来创建。 设置线程池的核心线程数(corePoolSize),最大线程数(maximumPoolSize),线程空闲回收时间(keepAliveTime)等参数。这些参数决定了线程池的大小和行为。 可选地设置任务队列,可以选择使用ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue等不同类型的队列,根据实际情况来选择最合适的。 提交任务给线程池执行,可以使用execute()方法提交一个Runnable任务,或者使用submit()方法提交一个Callable任务,可以获取任务的执行结果。 关闭线程池,当不再需要使用线程池时,应该调用线程池的shutdown()方法来关闭线程池。 3.线程池有什么运行机制,有什么参数? 启核心线程--->入队列--->启非核心线程--->异常 连接池线程处理不过来,有哪些处理方式? 抛出异常:当任务被提交到线程池中,但无法处理时,线程池会抛出异常。例如,当线程池的队列已满且无法接受更多任务时,线程池可以抛出 RejectedExecutionException 异常。在捕获异常时,可以选择记录日志、发送警报或执行其他适当的操作。 丢弃任务:当线程池无法处理任务时,可以选择丢弃该任务。这样可以减轻线程池的负担,但需要注意确保任务的丢失不会对系统造成重大影响。可以使用 DiscardPolicy 或者 DiscardOldestPolicy 策略来实现任务丢弃。 阻塞任务:当线程池无法处理任务时,可以选择将任务阻塞等待。这样可以保持任务的有序执行,直到有可用的线程来处理任务。可以使用 ArrayBlockingQueue 作为任务队列,通过设置队列的容量来控制线程池的负载。 调用者运行:当线程池无法处理任务时,可以选择由调用者自行处理任务。也就是说,任务由调用线程直接执行,而不是交给线程池处理。这样可以避免线程池负担过大,但也需要注意调用线程是否能够处理任务。 JVM: 1.java常见垃圾回收算法 标记-清除算法(Mark-and-Sweep):这是最基本的垃圾回收算法。它的过程分为两个阶段:标记阶段和清除阶段。首先从根节点开始,标记所有能够被访问到的对象,然后清除未标记的对象。标记-清除算法存在碎片问题,即内存空间会变得不连续,影响后续对象的分配。 复制算法(Copying):复制算法将内存一分为二,每次只使用其中一半。当一半空间被用满后,将存活的对象复制到另一半空闲空间中,然后清除已使用的空间。复制算法解决了碎片问题,但是需要一倍的内存空间,且对象的复制会影响性能。 标记-压缩算法(Mark-and-Compact):标记-压缩算法结合了标记-清除算法和复制算法的优点。它先标记存活对象,然后将存活对象紧凑地放置在内存的一端,然后对整个堆进行压缩,使得存活对象占用连续的内存空间。标记-压缩算法可以解决碎片问题,并且不需要额外的内存空间,但是需要移动存活对象,也会有一定的性能开销。 分代算法(Generational):分代算法基于对象的存活时间进行划分,一般将堆分为新生代和老年代。新生代通常使用复制算法,因为大多数对象在短时间内就会变成垃圾。老年代则使用标记-清除或标记-压缩算法,因为老年代中的对象通常存活时间较长。 2.你了解哪些垃圾收集器? ----这里限于篇幅,只是简要说明,强烈建议读者移步此处更详细地了解各个收集器原理(见章节:1.7.GC 垃圾收集器) Serial收集器:Serial收集器是最基本的垃圾回收器,它使用单线程进行垃圾回收。在进行垃圾回收时,会暂停所有的用户线程。Serial收集器适用于小型应用和客户端环境,它的特点是简单、高效,适用于单核处理器。 Parallel收集器:Parallel收集器使用多线程进行垃圾回收。Parallel收集器通过并行处理来提高垃圾回收的吞吐量,适用于多核处理器和数据量较大的应用。 CMS收集器:CMS(Concurrent Mark Sweep)收集器是一种并发垃圾回收器。它使用多线程和并发处理来最小化垃圾回收对应用程序暂停时间的影响。CMS收集器适用于对响应时间有较高要求的应用,但由于并发执行的特性,可能会增加垃圾回收器本身的开销。 G1收集器:G1(Garbage-First)收集器是一种面向服务器的垃圾回收器。它具有高吞吐量和可预测停顿时间的特点。G1收集器将内存分为多个小区域,并基于优先级进行垃圾回收。它可以通过调整停顿时间目标来平衡吞吐量和停顿时间。G1收集器适用于大内存容量和对停顿时间有较高要求的应用。 设计模式: 1.说三个设计模式,分别是什么意思? 你平时分别在什么情况下用到这三种设计模式? 单例模式 单例模式(Singleton Pattern)是一种创建型设计模式,用于保证一个类只有一个实例,并提供全局访问点。它的核心思想是通过限制类的实例化次数,保证在程序运行期间只存在一个实例。 实现单例模式的常用方法是使用一个私有的静态变量来存储实例对象,同时提供一个公共的静态方法来获取该实例。常见的实现方式有饿汉式(在类加载时即创建实例)和懒汉式(在使用时才创建实例)。 日志管理器:在一个应用程序中,通常只需要一个日志管理器来记录日志,单例模式可以确保只有一个实例用于记录日志。 数据库连接池:在多线程环境下,需要保证数据库连接池的全局唯一性,单例模式可以确保只有一个连接池实例。 工厂模式 工厂模式(Factory Pattern)是一种创建型设计模式,用于创建对象的统一接口。工厂模式通过将对象的实例化过程封装到一个工厂类中来解耦客户端与创建对象的过程。 数据库驱动管理器:在一个数据库操作的应用程序中,可以使用工厂模式来创建各种数据库驱动的实例。 代理模式 目标对象、代理对象和客户端。代理对象实现与目标对象相同的接口,并持有目标对象的引用。客户端通过代理对象来访问目标对象。 代理模式常见的应用场景是在访问对象的前后做一些额外处理,例如权限校验、延迟加载、远程访问等。使用代理模式可以增强目标对象的功能,同时对客户端屏蔽了目标对象的具体实现,增加了代码的灵活性和可维护性。 Spring: 1.spring中的拦截器和过滤器有什么区别? 触发时机: 过滤器:在Servlet容器中,过滤器是在请求进入Servlet容器之前和响应离开Servlet容器之前进行处理的,即在请求和响应的前后都触发执行。 拦截器:在Spring MVC框架中,拦截器是在请求进入Controller之前和离开Controller之后进行处理的,即只在请求处理的前后触发执行。 使用范围: 过滤器:过滤器可以用于所有的Web应用程序,无论是基于Spring的还是其他任何框架。它可以在应用程序的整个生命周期中使用。 拦截器:拦截器是Spring MVC框架特有的功能,只能用于使用了Spring MVC框架的应用程序中。 配置方式: 过滤器:过滤器的配置是在Web应用程序的web.xml文件中进行的,通过配置URL匹配模式来指定要拦截的请求。 拦截器:拦截器的配置是通过实现HandlerInterceptor接口,并在Spring配置文件中进行声明的,可以根据具体的请求路径等条件进行配置。 功能: 过滤器:过滤器可以对请求的参数进行处理、请求的编码进行转换、对响应结果进行过滤等。 拦截器:拦截器可以在请求进入Controller之前和离开Controller之后进行处理,可以进行权限验证、日志记录、性能监控等。 手撕算法部分: LeetCode347. 前 K 个高频元素
0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
下载 APP