原文链接:奇安信Java 1面经_牛客网 (nowcoder.com)
本文仅作为个人学习使用。
synchronized 和 Lock 的区别
synchronized
和 Lock
都是 Java 中用于实现线程同步的机制,但它们之间有一些区别:
-
语法层面
synchronized
是 Java 的关键字,可以修饰方法或代码块。Lock
是 Java 并发包java.util.concurrent.locks
中的接口,需要显式地调用lock()
和unlock()
方法来获取和释放锁。
-
灵活性
synchronized
是隐式锁,锁的获取和释放由 JVM 自动处理。Lock
是显式锁,开发者需要手动控制锁的获取和释放,提供了更细粒度的同步控制。
-
功能
synchronized
只能实现互斥同步,不可中断获取。Lock
提供了更丰富的功能,如读写锁、可中断的锁获取、超时获取锁等。
-
性能
- 由于
Lock
需要手动调用lock()
和unlock()
方法,增加了一些额外的操作开销。所以在低竞争环境下,synchronized
的性能通常优于Lock
。 - 由于
synchronized
不可中断获取,所以在高竞争环境下,使用Lock
可以获得更好的扩展性和伸缩性。Lock
提供了更灵活的加锁和解锁机制,可以减少线程阻塞。
- 由于
-
异常处理
- 使用
synchronized
时,如果发生异常,锁会自动释放。 - 使用
Lock
时,需要在finally
块中手动调用unlock()
方法释放锁,否则可能会导致死锁。
- 使用
总的来说,synchronized
是 Java 内置的同步机制,简单易用,但灵活性较低,Lock
是 Java 并发包提供的显式锁,功能更加丰富和灵活,但使用上也更加复杂。
synchronized 和 ReentrantLock 的区别
ReentrantLock
是 Java 并发包 java.util.concurrent.locks
中提供的一个显式锁实现,实现了lock接口,它有以下一些重要的特性:
-
可重入性
ReentrantLock
是一个可重入锁,即同一个线程可以多次获取同一个锁。这样可以避免死锁的发生。
-
公平性
ReentrantLock
支持公平锁和非公平锁两种实现方式。公平锁保证线程按照申请锁的顺序获取锁,而非公平锁则允许"插队"。synchronized
是非公平锁,无法控制线程获取锁的顺序。
-
可中断的锁获取
- 使用
lock.lockInterruptibly()
方法,线程在等待获取锁的过程中,可以响应中断信号,从而退出等待。
- 使用
-
超时获取锁
ReentrantLock
提供了tryLock()
和tryLock(long time, TimeUnit unit)
方法,允许线程在一定时间内尝试获取锁,如果超时未获取到锁则放弃。
-
条件变量
ReentrantLock
可以与Condition
接口配合使用,实现更灵活的线程同步和唤醒机制。
-
锁状态查询
ReentrantLock
提供了一系列方法,如isLocked()
、isHeldByCurrentThread()
、getHoldCount()
等,可以查询锁的当前状态。
-
性能
- 在高竞争环境下,
ReentrantLock
通常能提供更好的性能和伸缩性,因为它提供了更丰富的同步控制功能。
- 在高竞争环境下,
总的来说,ReentrantLock
是一个功能强大的显式锁实现,相比 synchronized
关键字,它提供了更细粒度的同步控制能力,在需要更复杂的同步需求时,是一个很好的选择。但同时也需要注意,使用 ReentrantLock
需要更多的手动操作,如显式的锁获取和释放,增加了开发的复杂度。
atomic 的原理
Atomic 类型在 Java 并发编程中扮演着重要的角色,它们的实现原理主要基于 CPU 提供的原子指令。Atomic 类型的主要实现原理如下:
-
原子操作
- Atomic 类型使用 CPU 提供的原子指令来实现对变量的原子操作,如
getAndIncrement()
、compareAndSet()
等。 - 这些原子指令能够在不被中断的情况下完成对共享变量的读-修改-写操作,从而保证操作的原子性。
- Atomic 类型使用 CPU 提供的原子指令来实现对变量的原子操作,如
-
乐观锁机制
- Atomic 类型通常使用乐观锁的方式来实现并发控制,即不依赖于全局锁来保护共享变量的访问。
- 在执行操作时,先检查共享变量的值是否发生变化,如果没有变化则更新成功,否则重试。
-
内存语义
- Atomic 类型的操作都会带有相应的内存语义,如
volatile
语义或happens-before
规则,确保操作的可见性和有序性。
- Atomic 类型的操作都会带有相应的内存语义,如
-
底层实现
- 不同的 Atomic 类型,如
AtomicInteger
、AtomicReference
等,底层实现可能会有所不同。 - 通常使用 Unsafe 类提供的本地方法,直接操作内存地址来实现原子操作。
- 不同的 Atomic 类型,如
-
JVM 优化
- JVM 会对 Atomic 类型的操作进行优化,如利用 CPU 指令集提供的 CAS 指令来实现原子操作,而不是使用锁。
- 这种优化可以在没有竞争的情况下,大幅提高并发性能。
总的来说,Atomic 类型的实现依赖于 CPU 硬件级别的原子指令,利用乐观锁的机制来实现高效的并发控制。这种实现方式相比使用全局锁,能够在没有竞争的情况下大幅提高并发性能。同时 Atomic 类型的操作也具有相应的内存语义保证,确保并发操作的正确性。