不稳定的StampedLock.unlock(长)行为?

Log*_*Mzz 7 java concurrency multithreading locking java-8

我正面临一个关于StampedLock的奇怪行为.以下是主要有问题的代码行:

StampedLock lock = new StampedLock();
long stamp1 = lock.readLock();
System.out.printf("Read lock count: %d%n", lock.getReadLockCount());
lock.unlock(stamp1 + 2);
System.out.printf("Read lock count: %d%n", lock.getReadLockCount());
Run Code Online (Sandbox Code Playgroud)

奇怪的行为是关于如何解锁"容忍"错误的阅读戳.你觉得这样对吗?


这里是参考的完整代码:

public class StampedLockExample {
  static StampedLock lock = new StampedLock();

  static void println(String message, Object... args) {
    System.out.printf(message, args);
    System.out.println();
  }

  static void printReadLockCount() {
    println("Lock count=%d", lock.getReadLockCount());
  }

  static long tryReadLock() {
    long stamp = lock.tryReadLock();
    println("Gets read lock (%d)", stamp);
    printReadLockCount();
    return stamp;
  }

  static long tryWriteLock() {
    long stamp = lock.tryWriteLock();
    println("Gets write lock (%d)", stamp);
    return stamp;
  }

  static long tryConvertToReadLock(long stamp) {
    long newOne = lock.tryConvertToReadLock(stamp);
    println("Gets read lock (%d -> %d)", stamp, newOne);
    printReadLockCount();
    return newOne;
  }

  static void tryUnlock(long stamp) {
    try {
      lock.unlock(stamp);
      println("Unlock (%d) successfully", stamp);
    } catch (IllegalMonitorStateException e) {
      println("Unlock (%d) failed", stamp);
    }
    printReadLockCount();
  }

  public static void main(String[] args) {
    println("%n--- Gets two read locks ---");
    long stamp1 = tryReadLock();
    long stamp2 = tryReadLock();
    long min = Math.min(stamp1, stamp2);
    long max = Math.max(stamp1, stamp2);

    println("%n--- Tries unlock (-1 / +2 / +4) ---");
    tryUnlock(min - 1);
    tryUnlock(max + 2);
    tryUnlock(max + 4);

    println("%n--- Gets write lock ---");
    long stamp3 = tryWriteLock();

    println("%n--- Tries unlock (-1 / +1) ---");
    tryUnlock(stamp3 - 1);
    tryUnlock(stamp3 + 1);

    println("%n--- Tries write > read conversion ---");
    long stamp4 = tryConvertToReadLock(stamp3);

    println("%n--- Tries unlock last write stamp (-1 / 0 / +1) ---");
    tryUnlock(stamp3 - 1);
    tryUnlock(stamp3);
    tryUnlock(stamp3 + 1);

    println("%n--- Tries unlock (-1 / +1) ---");
    tryUnlock(stamp4 - 1);
    tryUnlock(stamp4 + 1);
  }
}
Run Code Online (Sandbox Code Playgroud)

输出:

--- Gets two read locks ---
Gets read lock (257)
Lock count=1
Gets read lock (258)
Lock count=2

--- Tries unlock (-1 / +2 / +4) ---
Unlock (256) failed
Lock count=2
Unlock (260) successfully
Lock count=1
Unlock (262) successfully
Lock count=0

--- Gets write lock ---
Gets write lock (384)

--- Tries unlock (-1 / +1) ---
Unlock (383) failed
Lock count=0
Unlock (385) failed
Lock count=0

--- Tries write > read conversion ---
Gets read lock (384 -> 513)
Lock count=1

--- Tries unlock last write stamp (-1 / 0 / +1) ---
Unlock (383) failed
Lock count=1
Unlock (384) failed
Lock count=1
Unlock (385) failed
Lock count=1

--- Tries unlock (-1 / +1) ---
Unlock (512) failed
Lock count=1
Unlock (514) successfully
Lock count=0
Run Code Online (Sandbox Code Playgroud)

hee*_*nee 5

简短回答:

在戳记中添加两个是修改它的一部分,在读取模式锁定中不需要验证.

答案很长:

邮票包含两条信息:状态序列号,以及有多少读者.状态编号存储在印记的前57位中,读取器计数存储在最后7位中.因此,当您向图章添加2时,您将读取器计数从1更改为3,并保持状态编号不变.由于StampedLock仅在读取模式下获取,因此仅验证状态编号并忽略读取器计数.这是有道理的,因为读锁应该能够以任何顺序解锁.

例如:从现有的StampedLock获取读取标记,其状态编号为4,读取器计数为1.从同一个StampedLock获取第二个读取标记,状态编号为4,读取器计数为2.请注意,邮票的状态编号是相同的,因为StampedLock的状态在获取邮票之间没有变化.第一个读取标记用于解锁.第一个标记(4)的状态编号与StampedLock(4)的状态编号匹配,所以没关系.第一个标记(1)的读者数与StampedLock(2)的读者数不匹配,但这并不重要,因为读锁应该能够以任何顺序解锁.所以解锁成功了.

请注意,StampedLocks被设计为内部实用程序的高性能读/写锁,而不是承受恶意编码的东西,因此它在其预期的边界内运行.我认为解锁()的Javadoc 虽然有误导性.