大家好,我是鸭鸭!
亚马逊作为知名外企,福利待遇优渥,加上早 9 晚 6,双休,以及每周 2 天弹性居家办公的混合工作制,一度被称为神仙公司。
不过这个名号随着前两天新规定的宣布,大概要打上一个问号了。
从 2025 年 1 月 2 日起,亚马逊要求所有员工每周 5 天都需要在办公室工作。
对待居家办公,亚马逊就好像一个渣男,态度一变再变。22 年的时候还在疫情期间,亚马逊开始允许员工居家办公,并表示员工创造力不会受影响。但 23 年,亚马逊就开始收紧疫情期间的远程工作补贴,并强制要求所有员工每周返回办公室工作 3 天。
23 年的收紧政策就引发了亚马逊西雅图总部的员工示威。这回直接取消居家办公,不知道亚马逊员工会有什么样的反应。
神仙公司虽好,但一直当神仙公司,也不是一件容易的事情。 作为员工,最重要的还是得提升自己的核心竞争力,比如来找鸭鸭学习和刷题!
来看看今天鸭鸭为大家准备的面试题吧!
什么是 Java 的 StampedLock?
回答重点
StampedLock 是 Java 8 引入的一个锁机制,与传统的 ReentrantLock
和 ReadWriteLock
相比,StampedLock
通过引入乐观读锁和时间戳(stamp)的概念,提升了读写性能,尤其是在读多写少的场景下。
核心特性:
- 写锁(write lock):独占模式的锁,和
ReentrantLock
类似,保证写操作的排他性。 - 悲观读锁(read lock):共享模式的锁,多个线程可以同时持有读锁,但写锁需要等待。
- 乐观读锁(optimistic read lock):无需阻塞的读锁机制,允许在没有竞争的情况下进行快速读取。当检测到有写操作发生时,才会回退到悲观读锁或重试。
- 时间戳(stamp):
- 每个锁操作都会返回一个 stamp,代表了锁的状态,后续操作需要根据这个 stamp 来验证锁是否有效。
扩展知识
StampedLock 的优缺点
优点:
- 乐观读模式提供了更高效的并发读操作,避免了传统锁机制下的线程阻塞。
- 更灵活的锁机制,允许不同的锁模式进行切换,适合不同场景。
缺点:
- 不可重入:
StampedLock
是非重入锁,这意味着同一个线程不能在持有锁时再次获取同类型的锁,否则会造成死锁。 - 读锁饥饿:在高写入负载的场景下,悲观读锁可能会被长期阻塞,导致读操作饥饿。
- CPU飙升风险:如果线程使用
writeLock()
或者readLock()
获得锁之后,线程还没执行完就被interrupt()
的话,会导致CPU飙升,需要用readLockInterruptibly
或者writeLockInterruptibly
。
CPU 飙升示例代码
我本地跑了下复现了:
内部原理
StampedLock
通过一个长整型值来管理状态,低位用于表示锁的类型(写锁、读锁),高位用于表示锁的计数。当乐观读锁被获取时,生成一个时间戳(stamp),并在后续验证时判断是否有写操作发生。validate(stamp)
方法可以有效判断在持有乐观读锁的情况下,是否有其他写操作干扰(被修改了),从而决定是否要变为悲观读锁。
代码示例:
import java.util.concurrent.locks.StampedLock;
public class StampedLockExample {
private double x, y;
private final StampedLock lock = new StampedLock();
// 写操作:移动坐标
public void move(double deltaX, double deltaY) {
long stamp = lock.writeLock(); // 获取写锁
try {
x += deltaX;
y += deltaY;
} finally {
lock.unlockWrite(stamp); // 释放写锁
}
}
// 悲观读操作:获取坐标
public double distanceFromOrigin() {
long stamp = lock.readLock(); // 获取读锁
try {
return Math.sqrt(x * x + y * y);
} finally {
lock.unlockRead(stamp); // 释放读锁
}
}
// 乐观读操作:获取坐标
public double optimisticDistanceFromOrigin() {
long stamp = lock.tryOptimisticRead(); // 获取乐观读锁
double currentX = x, currentY = y;
if (!lock.validate(stamp)) { // 检查乐观读锁是否被其他写操作干扰
stamp = lock.readLock(); // 回退到悲观读锁
try {
currentX = x;
currentY = y;
} finally {
lock.unlockRead(stamp); // 释放读锁
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
}
关键点:
- 写锁用于修改共享资源,多个线程无法同时持有写锁。
- 悲观读锁保证数据一致性,适合写操作较多的场景。
- 乐观读锁适用于读多写少的场景,可以在大部分时间无锁读取,大大提高性能。
最后
再来推荐下鸭鸭的面试刷题网站和小程序:面试鸭!欢迎大家来刷题!