Java中常见锁的面试题与答案

什么是锁?

锁是多线程编程中的同步机制,用于控制多个线程对共享资源的访问。锁可以防止多个线程同时访问临界区,从而保证数据的一致性和正确性。

请介绍Java中的两种主要类型的锁。

ReentrantLock: 是一个可重入的锁,支持公平和非公平两种模式。它提供了更丰富的功能,如条件变量、可中断的锁获取以及超时获取等。 synchronized: 是Java中的内置锁,用于实现基本的同步。每个对象都有一个内置的监视器锁,可以通过synchronized关键字来获取和释放锁。

什么是死锁?如何避免死锁?

死锁是指多个线程相互等待彼此持有的资源而无法继续执行的情况。为避免死锁,可以采取以下措施:

  • 使用按序获取锁的策略,即规定线程获取锁的顺序,避免循环等待。

  • 设置超时时间,当线程无法获取锁时,可以放弃锁并进行其他处理。

  • 使用tryLock来尝试获取锁,如果获取失败,可以执行其他逻辑,避免长时间等待。

synchronized与ReentrantLock的区别是什么?

  • synchronized是Java的关键字,而ReentrantLock是一个类,提供了更多的灵活性和功能。
  • synchronized在发生异常时会自动释放锁,而ReentrantLock需要在finally块中显式释放锁。
  • ReentrantLock支持公平锁和非公平锁,而synchronized是非公平锁。
  • ReentrantLock可以尝试获取锁,并可以设置超时时间,而synchronized只能等待锁的释放。

什么是乐观锁和悲观锁?

  • 乐观锁: 假设多数情况下不会发生竞争,允许多个线程并发读取,但在更新数据时会检查是否有其他线程修改过数据。例如,CAS(Compare and Swap)操作就是一种乐观锁。
  • 悲观锁: 假设总是会发生竞争,每次访问数据都会加锁,从而保证数据的一致性。Java中的synchronized就是一种悲观锁。

什么是可重入锁?

可重入锁(Reentrant Lock)是指同一个线程在持有锁的情况下,可以再次获取该锁,而不会被阻塞。Java中的synchronized和ReentrantLock都是可重入锁。、

什么是公平锁和非公平锁?

  • 公平锁: 是指多个线程获取锁的顺序与它们请求锁的顺序一致,即按照先来先得的原则分配锁。
  • 非公平锁: 是指多个线程获取锁的顺序是不确定的,可能会导致某些线程长时间等待。非公平锁的效率通常比公平锁高,但可能导致某些线程饥饿。

请解释一下读写锁(ReadWriteLock)是什么?

读写锁是一种特殊的锁,用于控制对共享资源的读写操作。它支持多个线程同时读取共享资源,但只允许一个线程进行写操作。读写锁的设计在读多写少的场景中能够提高并发性能。

什么是StampedLock?

StampedLock是Java 8中引入的一种锁机制,它结合了读写锁和乐观锁的特性。它支持三种模式:读模式、写模式和乐观读模式。乐观读模式下,线程可以不阻塞地读取数据,如果在读取过程中数据被修改,则会返回失败。

如何选择合适的锁?

选择锁要根据具体的应用场景和性能需求进行考虑:

如果多数情况下是读操作,少数是写操作,可以考虑使用读写锁。 如果需要细粒度的锁控制,可以使用ReentrantLock。 如果对性能要求较高,可以使用乐观锁。 如果需要等待通知机制,可以使用synchronized。 如果需要更高级的功能,如超时获取、中断支持,可以使用ReentrantLock或StampedLock。

什么是CAS操作?

CAS(Compare and Swap)是一种乐观锁的操作,用于实现无锁并发控制。它是一种原子操作,用于比较某个内存位置的值与预期值,如果相等则将新值写入该位置,否则不执行任何操作。CAS操作常用于实现乐观锁,例如java.util.concurrent.atomic包中的原子类就使用了CAS操作来保证线程安全。

什么是ABA问题?如何解决?

ABA问题是指在使用CAS操作时,一个值从A变为B,然后再变回A。这可能导致使用CAS的线程误以为值未被修改。为了解决ABA问题,可以使用版本号(或标记位)来标识值是否发生过变化。例如,在AtomicStampedReference中,不仅比较值是否相等,还比较版本号是否相等。

Java中的锁机制有哪些应用场景?

  • 互斥锁(Mutex): 用于保护临界区,防止多线程同时访问共享资源。例如synchronized关键字、ReentrantLock等。
  • 读写锁(ReadWriteLock): 适用于读多写少的场景,提高并发性能。例如ReentrantReadWriteLock。
  • 乐观锁: 适用于并发读多写少的场景,减少锁的争用。例如CAS操作、Atomic类。
  • 分布式锁: 用于分布式系统中,保证多个节点之间的协调。例如基于数据库、ZooKeeper等的分布式锁。

什么是自旋锁?

自旋锁是一种基于忙等待的锁,当线程尝试获取锁时,如果锁已被其他线程占用,该线程会不断循环(自旋)等待锁的释放。自旋锁适用于临界区执行时间较短的情况,避免了线程切换带来的开销。

请解释一下可重入性(Reentrancy)是什么?

可重入性指的是同一个线程在持有锁的情况下,可以再次获取该锁,而不会被阻塞。这个特性允许在一个线程的方法调用链中,可以重复获取同一个锁,从而避免死锁的发生。

如何避免死锁?

避免死锁的一些常见方法包括:按序获取锁、设置获取锁的超时时间、使用死锁检测工具、尽量降低锁的粒度和持有时间、使用tryLock来获取锁等。

什么是公平性?

公平性指的是多个线程按照请求锁的顺序获得锁的机制。在公平锁中,等待时间较长的线程会更有机会获取锁,避免了某些线程一直被忽略的问题。但公平锁的性能可能较低,因为需要维护一个有序的等待队列。

什么是饥饿问题?

饥饿问题是指某些线程由于始终得不到满足条件而无法执行,一直处于等待的状态。这可能由于不公平的锁分配、线程优先级等因素导致。

什么是线程安全性?

线程安全性指的是多线程环境下,共享资源能够被多个线程安全地访问和修改,不会导致数据的不一致性或损坏。线程安全的代码能够正确处理多线程竞争的情况,保证程序的正确性。

什么是内部锁(Intrinsic Lock)?

内部锁是指由Java中的synchronized关键字提供的锁机制。每个对象都有一个内部锁,也称为监视器锁。通过synchronized关键字修饰的代码块或方法,可以获得对象的内部锁,从而保证同一时刻只有一个线程执行这些代码。

什么是外部锁(Explicit Lock)?

外部锁是通过java.util.concurrent.locks包中的锁对象(如ReentrantLock)提供的锁机制。外部锁相对于内部锁,提供了更多的功能,如可重入性、公平性、条件变量等,可以更精细地控制多线程并发访问。

请解释一下线程的阻塞和唤醒操作。

当线程尝试获取锁,但锁已被其他线程占用时,线程会进入阻塞状态,即暂停执行,等待锁的释放。当锁的持有者释放锁时,会唤醒一个或多个等待的线程,使其可以继续执行。

什么是忙等待(Busy Waiting)?

忙等待是指线程在等待某个条件满足时,不断地进行空循环,而不是进入阻塞状态。忙等待可能会占用CPU资源,降低系统性能,通常不推荐使用。

什么是线程调度?

线程调度是操作系统的一个重要功能,用于决定哪个线程在某个时间片内执行。线程调度可以采用不同的策略,如先来先服务、时间片轮转、优先级调度等,以满足不同线程的执行需求。

如何实现线程间的通信?

线程间的通信可以通过共享变量、锁、信号量、管道、消息队列等方式来实现。在Java中,可以使用synchronized关键字、wait和notify方法、Condition对象、阻塞队列等来实现线程间的协作和通信。

什么是线程池?为什么要使用线程池?

线程池是一组预先创建的线程,用于执行任务。使用线程池可以避免频繁地创建和销毁线程,减少系统开销,提高性能。线程池还可以控制并发线程的数量,避免过多线程的竞争和资源耗尽。

如何避免并发问题?

避免并发问题的一些常见方法包括:使用不可变对象、使用线程安全的数据结构、避免共享变量、合理使用锁、尽量降低锁的粒度、使用原子类、进行合理的线程设计等。

什么是线程局部变量(Thread-Local Variable)?

线程局部变量是指每个线程拥有自己的变量副本,不同线程之间互不影响。在Java中,可以通过ThreadLocal类来创建线程局部变量,常用于需要在线程间隔离数据的情况,如线程池中的任务。


原文始发于微信公众号(码农本农):Java中常见锁的面试题与答案

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/160616.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!