java面试题2024资源下载
### Java多线程面试知识点详解 #### 一、线程与进程的区别 - **定义**: - **进程**:操作系统中的最小运行单位,每个进程都有独立的内存空间。 - **线程**:进程内的执行序列,是调度和执行的基本单位。 - **资源占用**: - 进程间相互独立,拥有各自的地址空间;线程共享同一进程的资源,如内存空间。 - **上下文切换**: - 进程切换开销大,涉及用户态到内核态的切换以及进程上下文保存与恢复。 - 线程切换开销小,仅需保存和恢复少量寄存器和栈。 - **通信机制**: - 进程间通信复杂,通常需要通过IPC机制。 - 线程间可以直接访问对方的数据,无需额外同步措施。 #### 二、并行与并发的区别 - **并行**: - 指多个处理器同时执行多个任务。 - 强调的是硬件层面的支持。 - **并发**: - 在单个处理器上同时处理多个任务,但实际上是以分时方式快速切换执行。 - 更多地体现在软件层面的调度。 #### 三、线程创建方式 - **继承Thread类**: - 创建一个Thread类的子类,并重写run方法。 - 实例化子类对象,并调用start方法启动线程。 - **实现Runnable接口**: - 实现Runnable接口的run方法。 - 将实现类的对象作为参数传递给Thread类的构造函数,然后创建并启动线程。 - **实现Callable接口**: - Callable接口类似于Runnable,不同之处在于它有返回值。 - 使用FutureTask包装Callable,再将FutureTask传给Thread类构造函数创建线程。 - **使用线程池**: - 利用Executor框架创建线程池来管理和调度线程。 #### 四、如何停止一个正在运行的线程 - **设置中断标志**: - 调用线程的interrupt方法,设置线程的中断标志。 - 在线程体内部检查中断状态,如Thread.interrupted()或Thread.currentThread().isInterrupted()。 - **使用volatile变量**: - 在外部维护一个volatile类型的标记变量。 - 线程内部循环检查该变量是否为true,如果是则退出循环。 - **不推荐的方法**: - stop方法已被废弃,可能导致资源泄露和不可预料的行为。 #### 五、ReentrantLock的实现原理 - **基于AQS框架**: - ReentrantLock通过自定义AQS的状态值来控制锁的获取和释放。 - **公平锁与非公平锁**: - 公平锁遵循先来后到的原则,而非公平锁可能会优先唤醒刚释放锁的线程。 - **独占锁与共享锁**: - 支持多种锁模式,包括独占锁和共享锁。 #### 六、线程的run()与start()方法的区别 - **run()**: - 直接调用run方法相当于调用普通方法。 - 不会启动新线程,而是在当前线程中执行。 - **start()**: - 启动新线程,并调用run方法。 - 实际上是由JVM调度线程去执行run方法。 #### 七、AQS(AbstractQueuedSynchronizer) - **概念**: - AQS是实现锁和其他同步工具的基础框架。 - 定义了一套多线程访问共享资源的同步器框架。 - **核心功能**: - 管理线程等待队列,包括等待队列的构建、等待节点的插入和移除。 - 提供了独占式和共享式两种同步器模板。 #### 八、notify()与notifyAll()的区别 - **notify()**: - 唤醒等待队列中的一个线程。 - 需要注意的是,哪个线程被唤醒是非确定性的。 - **notifyAll()**: - 唤醒等待队列中的所有线程。 - 可以避免因特定线程未被唤醒而导致的“假死”问题。 #### 九、如何保证三个线程按顺序执行 - **使用CountDownLatch**: - 创建CountDownLatch对象,计数器设为2。 - 第一线程执行完毕后调用countDown,计数器减1。 - 第二线程执行完毕后再次调用countDown,计数器减1。 - 第三线程等待计数器变为0后开始执行。 - **使用CyclicBarrier**: - 创建CyclicBarrier对象,屏障数设为3。 - 三个线程分别到达屏障后,都会等待其他线程到达。 - 当三个线程都到达屏障时,所有线程继续执行。 #### 十、如何控制方法允许并发访问线程的数量 - **使用Semaphore**: - 创建Semaphore对象,许可数量代表允许的最大并发数。 - 在进入临界区前调用acquire获取许可,离开临界区后调用release释放许可。 #### 十一、为什么不建议用Executors创建线程池 - **默认无界队列**: - Executors提供的一些工厂方法默认使用无界队列,可能导致OOM。 - **配置不当**: - 如果没有正确配置线程池参数,可能会导致性能问题或资源泄露。 #### 十二、CAS(Compare and Swap) - **概念**: - 一种原子更新操作,用于实现乐观锁。 - 在不加锁的情况下完成对内存位置的更新。 - **实现原理**: - 原子地比较内存位置V的期望值A和提供的更新值B。 - 如果V的值等于A,则用B更新V;否则不做任何操作。 - **优缺点**: - 优点:减少锁的使用,提高并发性能。 - 缺点:可能导致ABA问题,需要额外机制解决。 #### 十三、JMM(Java Memory Model) - **概念**: - 规定了程序中的各种变量(线程共享变量)的访问规则。 - 描述了主内存与线程工作内存之间的交互协议。 - **主要特性**: - 原子性、可见性和有序性。 - 保证了线程间数据的一致性。 #### 十四、线程池的种类 - **FixedThreadPool**: - 创建固定大小的线程池。 - 适用于需要控制最大并发数的任务。 - **CachedThreadPool**: - 创建可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程。 - 适用于执行很多短期异步任务的情况。 - **ScheduledThreadPoolExecutor**: - 创建定时任务线程池,支持周期性执行任务。 #### 十五、synchronized关键字的底层原理 - **监视器锁**: - synchronized关键字实现依赖于JVM的Monitor对象。 - 每个对象有一个Monitor与之关联,当且只有一个线程能持有Monitor。 - **偏向锁、轻量级锁和重量级锁**: - synchronized锁升级过程:偏向锁→轻量级锁→重量级锁。 - 在不同的竞争压力下采用不同的锁策略,以达到最优的锁性能。 #### 十六、如何进行死锁诊断 - **使用jstack工具**: - jstack可以输出每个线程的堆栈跟踪。 - 通过分析线程状态,判断是否存在死锁。 - **查看线程状态**: - 死锁通常发生在两个或多个线程互相等待对方持有的锁。 - 查看线程等待锁的情况,识别死锁环路。 #### 十七、死锁产生的条件 - **互斥条件**: - 资源必须互斥使用。 - **请求与保持条件**: - 已经保持至少一个资源的进程可能请求新的资源。 - **不剥夺条件**: - 已分配的资源不能被剥夺,只能由使用它的进程显式释放。 - **循环等待条件**: - 存在一个进程等待链,其中第一个进程等待被最后一个进程占用的资源。 #### 十八、线程的状态及其变化 - **新建状态**: - 线程对象创建后,还没有调用start方法。 - **就绪状态**: - 线程对象调用start方法,尚未被调度。 - **运行状态**: - 线程获得CPU使用权,正在执行。 - **阻塞状态**: - 线程因为等待某种条件发生而暂停执行。 - **死亡状态**: - 线程体执行结束或被异常终止。 #### 十九、synchronized与Lock的区别 - **语义差异**: - synchronized属于JVM级别的锁,而Lock是通过API实现的。 - **灵活性**: - Lock提供了比synchronized更多的功能,如尝试锁、公平锁等。 - **可重入性**: - synchronized和Lock都可以实现可重入,但Lock需要手动实现。 - **异常处理**: - synchronized在异常自动释放锁,而Lock需要显式释放锁。 #### 二十、线程池的核心参数 - **核心线程数**: - 线程池维护的最小线程数。 - **最大线程数**: - 线程池允许创建的最大线程数。 - **空闲线程存活时间**: - 空闲线程等待新任务的最长时间。 - **工作队列**: - 用于存放等待执行的任务。 - **拒绝策略**: - 当线程池无法接受更多任务时的处理方式。 #### 二十一、ConcurrentHashMap - **特点**: - 高效的线程安全的哈希表。 - 使用分段锁技术,将数据分成若干段,每段使用一个锁。 - **应用场景**: - 适合大量读取操作,少量写入操作的场景。 #### 二十二、volatile关键字的理解 - **作用**: - 保证了变量的可见性和禁止指令重排序。 - **可见性**: - 当一个线程修改了volatile变量后,其他线程能够立即看到最新的值。 - **禁止指令重排序**: - volatile修饰的变量不会被编译器优化。 #### 二十三、导致并发程序出现问题的根本原因 - **共享数据**: - 多个线程访问共享资源可能导致竞态条件。 - **指令重排序**: - 编译器或处理器可能会为了优化而重新排序指令。 - **上下文切换**: - 线程间频繁切换可能导致数据不一致。 #### 二十四、如何确定核心线程数 - **经验法则**: - 通常设置为核心数+1。 - **性能测试**: - 通过实际运行测试来确定最优的核心线程数。 #### 二十五、runnable与callable的区别 - **return类型**: - Runnable没有返回值,Callable有返回值。 - **执行方式**: - Runnable可以通过Thread类的构造函数直接创建线程执行。 - Callable需要通过FutureTask包装后才能提交给ExecutorService执行。 #### 二十六、ThreadLocal的理解 - **概念**: - ThreadLocal提供线程本地变量,每个线程都有自己的副本。 - **用途**: - 解决线程间的变量隔离问题。 - **实现原理**: - 利用Thread类的ThreadLocalMap存储变量。 #### 二十七、Java中wait与sleep方法的不同 - **所属类**: - wait属于Object类,sleep属于Thread类。 - **锁释放**: - wait会释放对象锁,sleep不会释放对象锁。 - **中断响应**: - wait可以被中断,sleep也可以被中断。 #### 二十八、线程池中常见的阻塞队列 - **ArrayBlockingQueue**: - 基于数组结构的有界阻塞队列。 - **LinkedBlockingQueue**: - 基于链表结构的阻塞队列。 - **PriorityBlockingQueue**: - 具有优先级的无界阻塞队列。 - **SynchronousQueue**: - 不存储元素的阻塞队列。 以上内容覆盖了Java多线程面试中的常见问题及知识点,希望能帮助你更好地准备面试。