可重入读写锁。读取和写入获取优先级。

3

我研究了ReentrantReadWriteLock。

Java文档中的片段如下:

直到最老的正在等待的写线程获取并释放写锁之后,当前线程才会获取读锁

因此,我的理解是:

读持续时间- 1个时间单位

写持续时间- 3个时间单位

  1. 时间0 - 获取写锁
  2. 时间1 - 尝试读取读锁
  3. 时间2 - 尝试写入写锁

因此,我期望以下顺序:

  1. 第一个写
  2. 第二个写
  3. 读取

我的实验代码:

public class RWLockCalculator {
    static long initTime = System.currentTimeMillis();
    private static int calculatedValue = 0;
    private static ReadWriteLock lock = new ReentrantReadWriteLock();
    public void calculate(int value) {
        lock.writeLock().lock();
        try {           
            System.out.println("write lock acquired at "+ (System.currentTimeMillis()-RWLockCalculator.initTime));
            this.calculatedValue = 1;
            Thread.sleep(300);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            lock.writeLock().unlock();
        }
    }

    public int getCalculatedValue() {
        lock.readLock().lock();
        try {           
            System.out.println("read lock acquired at "+ (System.currentTimeMillis()-RWLockCalculator.initTime));
            Thread.sleep(100);
            return calculatedValue;
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return -1;
        } finally {
            lock.readLock().unlock();
        }
    }
}

class Test {
    public static void main(String[] args) throws InterruptedException {
        new WriteThread().start();
        Thread.sleep(100);
        new ReadThread().start();
        Thread.sleep(100);
        new WriteThread().start();

    }
}

class ReadThread extends Thread {
    @Override
    public void run() {
        System.out.println(new RWLockCalculator().getCalculatedValue() + ", " + (System.currentTimeMillis() - RWLockCalculator.initTime));
    }
}

class WriteThread extends Thread {
    @Override
    public void run() {
        new RWLockCalculator().calculate(99);
        System.out.println("I have written in  " + (System.currentTimeMillis() - RWLockCalculator.initTime));
    }
}

输出:

write lock acquired at 0
I have written in  300
read lock acquired at 300
1, 400
write lock acquired at 400
I have written in  700

因此,我得到了以下结果:
  1. 首先写入
  2. 读取
  3. 第二次写入
为什么会得到这个结果?
是否有可能打破FIFO排序?
更新
请比较来自Java文档的两个兄弟代码片段(关于公平模式):
第一段:
如果一个线程尝试非重入地获取公平读锁,则会在写锁已经被持有或有等待的写线程的情况下阻塞。直到最老的正在等待的写线程获取并释放写锁后,该线程才会获得读锁。当然,如果等待的写线程放弃等待,将一个或多个读线程作为队列中等待时间最长的读者分配到可用的写锁,则这些读线程将被分配读锁。
第二段:
如果一个线程尝试非重入地获取公平写锁,则除非读锁和写锁都是空闲的(这意味着没有等待的线程),否则它将被阻塞。(请注意,非阻塞的ReentrantReadWriteLock.ReadLock.tryLock()和ReentrantReadWriteLock.WriteLock.tryLock()方法不遵循这个公平设置,如果可能,无论等待线程如何,都会获取锁。)
我没有完全理解那里所写的意思,但我看到ReentrantReadWriteLock对获取读锁和写锁使用了不同的策略。我建议,如果Java文档中的两个缩进方式相同,那么它们的政策也应该相同。
ReadLock可以共享锁。这是唯一的区别吗?

我认为,“当前等待时间最长的写入线程”是读取线程请求锁定时的那个线程。因此,在您的示例中没有等待的写入线程,读取器是下一个。 - SpaceTrucker
@SpaceTrucker 这只是公平的吗?先进先出? - gstackoverflow
1个回答

3
首先,应该以公平模式创建ReentrantReadWriteLock以强制执行锁获取的特定顺序:
private static ReadWriteLock lock = new ReentrantReadWriteLock(true);

然后,javadoc明确地描述了您的情况:
当以公平方式构建时,线程将使用近似到达顺序的策略来争夺进入。 当当前持有锁被释放时,最长等待的单个写线程将被分配写锁,或者如果有一组读线程等待时间比所有等待的写线程都要长,则该组将被分配读锁。
由于您的读取线程等待时间比第二个写入线程长,因此它会在写入线程之前获得锁定。

能否打破FIFO排序? - gstackoverflow
所有线程都排在一个公共队列中。无论是写锁还是读锁,它都想要获取吗? - gstackoverflow
我不擅长英语。似乎无论等待流(读取或写入)的锁是什么,它们都排队等候。当队列到达时,锁定被捕获吗? - gstackoverflow
读写锁定策略必须不同,因为写锁是独占的,而读锁不是。据我所知,它基本上作为一个FIFO队列,为写线程和读者线程组(可能共享锁)提供服务。 - axtavt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接