面试 Java 并发编程八股文十问十答第十四期
相信看了本文后,对你的面试是有一定帮助的!关注专栏后就能收到持续更新!
⭐点赞⭐收藏⭐不迷路!⭐
1)ForkJoinPool 有了解过吗?
是的,我了解 ForkJoinPool。它是 Java 并发包中提供的一个用于并行执行任务的框架,通常用于处理分而治之的任务。ForkJoinPool 使用工作窃取算法(work-stealing)来提高并行任务的执行效率,通过将大任务拆分成小任务,并行执行这些小任务,然后合并结果来完成整个任务的执行。在 Java 7 中引入,主要用于处理递归性的、数据量大的并行任务。
2)如果现在有多个线程并发执行,如何控制它们的执行顺序?
在 Java 中,可以使用各种同步机制来控制多个线程的执行顺序,比如使用 synchronized 关键字、ReentrantLock、CountDownLatch、CyclicBarrier 等。具体的控制方式取决于应用场景和需求,可以通过线程的等待、唤醒、阻塞、解锁等操作来实现线程的有序执行。
3)Java 中的阻塞队列用过哪些?
Java 中常用的阻塞队列包括:
- ArrayBlockingQueue: 基于数组的有界阻塞队列,固定大小,一旦队列满了,插入操作会阻塞,直到队列有空间;一旦队列为空,移除操作会阻塞,直到队列有元素。
- LinkedBlockingQueue: 基于链表的可选有界或无界阻塞队列,默认是无界的,可以通过构造函数指定容量。与 ArrayBlockingQueue 不同,LinkedBlockingQueue 在插入和移除时不会对整个队列进行锁定。
- PriorityBlockingQueue: 基于优先级堆的无界阻塞队列,元素按照优先级顺序被移除,插入时也可以指定优先级。
- DelayQueue: 基于优先级堆的延时阻塞队列,用于存放实现了 Delayed 接口的元素,只有在延时期满时才能从队列中取出元素。
- SynchronousQueue: 一个不存储元素的阻塞队列,每个插入操作必须等待一个对应的移除操作,反之亦然,主要用于线程间的数据交换。
这些阻塞队列都是线程安全的,可以在多线程环境下安全地使用。选择适当的队列取决于具体的需求和场景,比如是否需要有界队列、是否需要按照优先级顺序等。
4)用过Java 中哪些原子类?
Java 中的原子类主要用于在多线程环境下进行原子操作,保证操作的完整性和一致性,常用的原子类包括:
- AtomicInteger: 提供了原子更新整型的操作,比如自增、自减等。
- AtomicLong: 提供了原子更新长整型的操作。
- AtomicBoolean: 提供了原子更新布尔类型的操作。
- AtomicReference: 提供了原子更新引用类型的操作。
- AtomicReferenceFieldUpdater: 可以对指定类的指定字段进行原子更新。
- AtomicIntegerFieldUpdater: 对指定类的整型字段进行原子更新。
- AtomicLongFieldUpdater: 对指定类的长整型字段进行原子更新。
- AtomicStampedReference: 带有版本号的原子引用,可以解决 CAS 操作的 ABA 问题。
这些原子类都是线程安全的,可以在多线程环境下安全地进行操作,避免了使用 synchronized 或者 Lock 进行手动同步的开销和复杂性。
5)用过 Java 的累加器吗?
Java 中的累加器通常是指 LongAdder 和 DoubleAdder。它们是在 Java 8 中引入的原子累加器,用于在高并发情况下进行累加操作。与普通的原子类(如 AtomicInteger)不同,LongAdder 和 DoubleAdder 使用了一种分段的方式来减少竞争,从而提高了性能。
LongAdder 和 DoubleAdder 主要用于累加操作,比如统计数据量、计算总和等场景。它们提供了 add 方法用于增加值,sum 方法用于获取当前累加的总和,以及 reset 方法用于重置累加器的值。
6)Synchronized 和 ReentrantLock 有什么区别?
Synchronized 和 ReentrantLock 都是 Java 中用于实现线程同步的机制,但它们有一些区别:
- 实现方式: Synchronized 是 JVM 实现的,属于 Java 关键字;而 ReentrantLock 是基于 Java API 实现的类。
- 锁的获取方式: Synchronized 是隐式获取锁的,当线程进入 synchronized 代码块时,会自动获取锁,并在代码块执行完成后释放锁;而 ReentrantLock 需要显式地调用 lock 方法来获取锁,并且需要在 finally 块中调用 unlock 方法来释放锁。
- 可中断性: ReentrantLock 提供了可中断的获取锁方式,即可以在等待锁的过程中响应中断;而 Synchronized 不支持中断。
- 公平性: ReentrantLock 可以选择是否公平地获取锁,通过构造函数传入 true 来创建一个公平锁;而 Synchronized 是非公平的,无法控制。
- 条件变量: ReentrantLock 提供了 Condition 来实现线程等待/唤醒机制,可以精确控制线程的等待和唤醒;而 Synchronized 没有这样的功能,只能使用 wait 和 notify/notifyAll 方法来实现简单的等待/唤醒。
总的来说,ReentrantLock 提供了更灵活、更强大的线程同步机制,但使用起来也更加复杂,需要显式地管理锁的获取和释放;而 Synchronized 使用起来更简单,但功能相对较少,灵活性也较差。
7)Synchronized 原理知道不?
Synchronized 是 Java 中用于实现线程同步的关键字,它可以应用于方法或代码块,保证了多个线程对共享资源的安全访问。Synchronized 的原理主要基于对象头和监视器对象(Monitor)。
当一个线程进入 Synchronized 方法或代码块时,会尝试获取对象的监视器(Monitor)锁。如果这个锁已经被其他线程持有,那么当前线程会被阻塞,直到获取到锁为止。当线程执行完 Synchronized 方法或代码块后,会释放监视器锁,其他等待的线程就有机会获取锁进入临界区执行。
8)有深入分析过 Synchronized 底层原理吗?
Synchronized 的底层实现涉及到对象头的标记位和监视器锁的获取与释放机制。在 Java HotSpot 虚拟机中,Synchronized 的底层实现分为三种状态:无锁状态、偏向锁状态和轻量级锁状态。
- 无锁状态: 当对象被创建时,对象头的 Mark Word 中的锁标志位为 00,表示无锁状态。这时候,任何线程都可以通过 CAS 操作尝试获取锁。
- 偏向锁状态: 当对象被某个线程获取到锁后,对象头的 Mark Word 中的锁标志位变为 01,表示偏向锁状态。这时候,如果同一个线程再次进入临界区,就不需要再次进行加锁操作,可以直接进入,提高了效率。
- 轻量级锁状态: 当有多个线程竞争同一个锁时,会进入轻量级锁状态。在这个状态下,线程会尝试使用 CAS 操作来获取锁,如果成功则将对象头中的锁标志位设置为指向线程栈中锁记录的指针,如果失败则会膨胀为重量级锁。
9)Synchronized轻量级锁升级会自旋?
是的,Synchronized 在轻量级锁状态下会进行自旋操作,尝试在用户态进行快速的锁竞争。当一个线程尝试获取轻量级锁失败时,会进入自旋状态,即在循环中不断尝试获取锁。自旋的目的是为了减少线程进入内核态的次数,提高锁的竞争效率。如果在一定次数的自旋之后还没有获取到锁,线程就会膨胀为重量级锁,进入阻塞状态。
10)Synchronized 升级到重量级锁会怎样?
当 Synchronized 升级到重量级锁时,会涉及到线程的阻塞和唤醒操作,这将引入更大的性能开销。在重量级锁状态下,如果一个线程持有锁,而另一个线程想要获取同一把锁,那么后者的线程将会进入阻塞状态,而不是像轻量级锁那样自旋。
重量级锁的实现通常会涉及到操作系统的底层调用,比如线程的挂起和唤醒,这样的开销相对较大。因此,尽量避免锁竞争,减少锁的持有时间,是提高并发性能的一种重要策略。
开源项目地址:https://gitee.com/falle22222n-leaves/vue_-book-manage-system
前后端总计已经 1300+ Star,2 W+ 访问!
⭐点赞⭐收藏⭐不迷路!⭐