我正在研究Java代码,我需要在其中实现线程。我正在通过JAVA 8 API,我开始了解Stamped Locks。谁能告诉我为什么在多线程中使用StampedLocks?
提前致谢。
答案 0 :(得分:20)
StampedLock是使用ReadWriteLock(由ReentrantReadWriteLock实现)的替代方法。 StampedLock和ReentrantReadWriteLock之间的主要区别在于:
因此,如果你有一个你有争用的场景(否则你可以使用synchronized
或简单的Lock
)和更多的读者而不是作家,使用StampedLock可以显着提高性能。
但是,在得出结论之前,您应该根据具体的用例来衡量效果。
Heinz Kabutz撰写了关于StampedLocks in his newsletter的文章,他还撰写了a presentation about performance。
答案 1 :(得分:5)
java.util.concurrent.locks.StampedLock的API文档说:
StampedLocks设计用作开发线程安全组件的内部实用程序。它们的使用依赖于对它们所保护的数据,对象和方法的内部属性的了解。它们不是可重入的,因此锁定的主体不应该调用可能尝试重新获取锁的其他未知方法(尽管您可以将戳记传递给可以使用或转换它的其他方法)。读锁定模式的使用依赖于相关的代码部分是无副作用的。未经验证的乐观读取部分无法调用未知容忍潜在不一致的方法。标记使用有限表示,并且不是加密安全的(即,可以猜测有效标记)。印花值可在连续操作一年后(不迟于一年)后回收。未经使用或验证超过此期限而持有的印章可能无法正确验证。 StampedLocks是可序列化的,但总是反序列化为初始解锁状态,因此它们对远程锁定没有用。
e.g。 -
class Point {
private double x, y;
private final StampedLock sl = new StampedLock();
void move(double deltaX, double deltaY) { // an exclusively locked method
long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp);
}
}
double distanceFromOrigin() { // A read-only method
long stamp = sl.tryOptimisticRead();
double currentX = x, currentY = y;
if (!sl.validate(stamp)) {
stamp = sl.readLock();
try {
currentX = x;
currentY = y;
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
void moveIfAtOrigin(double newX, double newY) { // upgrade
// Could instead start with optimistic, not read mode
long stamp = sl.readLock();
try {
while (x == 0.0 && y == 0.0) {
long ws = sl.tryConvertToWriteLock(stamp);
if (ws != 0L) {
stamp = ws;
x = newX;
y = newY;
break;
}
else {
sl.unlockRead(stamp);
stamp = sl.writeLock();
}
}
} finally {
sl.unlock(stamp);
}
}
}
答案 2 :(得分:1)
StampedLock支持读写锁。与ReadWriteLock相比,StampedLock的锁定方法返回由long值表示的戳记。您可以使用这些标记释放锁定或检查锁定是否仍然有效。另外,标记锁支持另一种称为乐观锁定的锁定模式。
ExecutorService executor = Executors.newFixedThreadPool(2);
Map<String, String> map = new HashMap<>();
StampedLock lock = new StampedLock();
executor.submit(() -> {
long stamp = lock.writeLock();
try {
Thread.sleep(100);
map.put("test", "INDIA");
} catch (Exception e) {
} finally {
lock.unlockWrite(stamp);
}
});
Runnable readTask = () -> {
long stamp = lock.readLock();
try {
System.out.println(map.get("test"));
Thread.sleep(100);
} catch (Exception e) {
} finally {
lock.unlockRead(stamp);
}
};
executor.submit(readTask);
executor.submit(readTask);
通过readLock()或writeLock()获取读或写锁定返回一个标记,该标记稍后用于在finally块中解锁。请记住,加盖锁不会实现折返特性。每次锁定调用都会返回一个新的标记,如果没有锁定可用则会阻塞,即使同一个线程已经拥有锁定。所以你必须特别注意不要遇到死锁。
executor.submit(() -> {
long stamp = lock.tryOptimisticRead();
try {
System.out.println("Optimistic Lock Valid: " + lock.validate(stamp));
Thread.sleep(100);
System.out.println("Optimistic Lock Valid: " + lock.validate(stamp));
Thread.sleep(1000);
System.out.println("Optimistic Lock Valid: " + lock.validate(stamp));
} catch (Exception e) {
} finally {
lock.unlock(stamp);
}
});
executor.submit(() -> {
long stamp = lock.writeLock();
try {
System.out.println("Write Lock acquired");
Thread.sleep(100);
} catch (Exception e) {
} finally {
lock.unlock(stamp);
System.out.println("Write done");
}
});
通过调用tryOptimisticRead()获取乐观读锁定,无论锁定是否实际可用,它都会在不阻塞当前线程的情况下返回戳记。如果已经有写锁定激活,则返回的戳记等于零。您始终可以通过调用lock.validate(stamp)来检查戳记是否有效。
答案 3 :(得分:0)
除了@assylias answer:
StampedLock支持使用方法tryConvertToWriteLock(long)
将readLock升级为writeLock:
此类还支持有条件提供的方法 三种模式之间的转化。例如方法 tryConvertToWriteLock(long)尝试“升级”模式,返回一个 如果(1)已经处于写入模式(2)处于读取模式,则有效的写入标记 并且没有其他阅读器,或者(3)处于乐观模式并且处于锁定状态 可用。这些方法的形式旨在帮助减少 在基于重试的设计中可能会发生的一些代码膨胀。
代码示例:
class Point {
private double x, y;
private final StampedLock sl = new StampedLock();
// an exclusively locked method
void move(double deltaX, double deltaY) {
long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp);
}
}
// a read-only method
// upgrade from optimistic read to read lock
double distanceFromOrigin() {
long stamp = sl.tryOptimisticRead();
try {
retryHoldingLock: for (;; stamp = sl.readLock()) {
if (stamp == 0L)
continue retryHoldingLock;
// possibly racy reads
double currentX = x;
double currentY = y;
if (!sl.validate(stamp))
continue retryHoldingLock;
return Math.hypot(currentX, currentY);
}
} finally {
if (StampedLock.isReadLockStamp(stamp))
sl.unlockRead(stamp);
}
}
// upgrade from optimistic read to write lock
void moveIfAtOrigin(double newX, double newY) {
long stamp = sl.tryOptimisticRead();
try {
retryHoldingLock: for (;; stamp = sl.writeLock()) {
if (stamp == 0L)
continue retryHoldingLock;
// possibly racy reads
double currentX = x;
double currentY = y;
if (!sl.validate(stamp))
continue retryHoldingLock;
if (currentX != 0.0 || currentY != 0.0)
break;
stamp = sl.tryConvertToWriteLock(stamp);
if (stamp == 0L)
continue retryHoldingLock;
// exclusive access
x = newX;
y = newY;
return;
}
} finally {
if (StampedLock.isWriteLockStamp(stamp))
sl.unlockWrite(stamp);
}
}
// Upgrade read lock to write lock
void moveIfAtOrigin(double newX, double newY) {
long stamp = sl.readLock();
try {
while (x == 0.0 && y == 0.0) {
long ws = sl.tryConvertToWriteLock(stamp);
if (ws != 0L) {
stamp = ws;
x = newX;
y = newY;
break;
}
else {
sl.unlockRead(stamp);
stamp = sl.writeLock();
}
}
} finally {
sl.unlock(stamp);
}
}
}