可升级的readlock的优势?

我想知道使用可升级读锁的优点是什么,而不是执行以下步骤:

  1. 读取锁定
  2. 检查条件,看看我们是否需要写锁定
  3. 释放读锁定
  4. 采取写锁定
  5. 执行更新
  6. 释放写锁定

与采用可升级的读锁相反,执行上述步骤的一个明显缺点是,在步骤3和4之间存在时间窗口,其中另一个线程可以占用写锁定。

除了这个优势之外,您还发现了其他优势,可以通过我上面提到的步骤获得可升级的读锁定function吗?

让我们考虑一下可以使用没有单独的“可升级读卡器”的读写器锁的不同方式。

对于您的模式,当您指出时,在步骤3和4之间存在竞争,其中另一个线程可以使用写入程序锁定。 更重要的是,在3到4之间有一个步骤,其中一个线程可以获取写入器锁定并更改我们在步骤2中观察到的状态

因此,我们有四种选择,具体取决于它是如何发生的:

  1. 我们坚持你的方法,因为这实际上是不可能的(例如,在我们的应用中,给定的状态转换是单向的,所以一旦观察到它是永久的)。 在这种情况下,虽然我们很可能已经重新设计,以便根本不需要锁。 (单向转换适用于无锁技术)。

  2. 我们只是首先使用编写器锁,因为我们在步骤2中观察到的状态很可能会发生变化,并且用读取器锁定检查它是浪费时间。

  3. 我们将您的步骤更改为:

    1. 读取锁定
    2. 检查条件,看看我们是否需要写锁定
    3. 释放读锁定
    4. 采取写锁定
    5. 如果发生变化,请重新检查条件。
    6. 执行更新
    7. 释放写锁定
  4. 我们改为:

    1. 对支持递归的锁进行读锁定。
    2. 检查我们是否需要写入锁定。
    3. 写锁定(不释放读取)。
    4. 执行更新。
    5. 释放写锁定。
    6. 释放读锁定。

不难看出为什么4对某些人来说更具吸引力,尽管只是稍微难以看出它如何使死锁易于创建。 可悲的是,稍微用力就足以让很多人看到优势而不会看到劣势。

对于没有发现它的人来说,如果两个线程有​​一个读锁定,其中一个线程升级到写锁定,它必须等待另一个线程释放读锁定。 但是,如果第二个线程在没有释放读锁的情况下升级到写锁,那么它将在第一个线程上永远等待,这将永远等待它。


如上所述,哪种方法最好取决于国家在此期间改变的可能性(或者我们想要对它做出多快反应,我想)。 即使是非发布升级的最后一种方法也可以在可行代码中占有一席之地,只要有一个线程可以尝试升级其锁而不释放。

除了最后一个选项有效的特殊情况之外,其他选项之间的差异都与性能有关,哪个性能最高,主要取决于重新检查状态的成本以及由于更改而导致写入中止的可能性同时。

但是,请注意,所有这些都涉及获取编写器锁,因此所有这些都具有阻止所有读取线程的效果,即使写入确实已中止。

可升级的读锁为我们提供了中间地带,因为它们阻止了写锁和其他可升级的读锁,但它们不会阻止读锁。 它们可能更好,虽然不是可以升级为尚未提交写入的写锁的读锁。*在决定不升级的情况下,对读取线程的影响为零。

这意味着如果线程甚至可能稍微决定不改变状态,那么读取线程不会受到影响,并且性能的提高肯定可以certificate它的使用是合理的。

*就此而言,“读写器”有点用词不当,我们可以用ReaderWriterLockSlim保护一个int或对象数组,使用读锁来primefaces地读取和写入单个项目并使用写锁定对于需要读取整个数组而不会在读取时更改部分的操作。 在这种情况下,它是一个读取操作,而不是需要独占锁,而共享锁的写操作很好。

它还可以防止可能发生的死锁,因为不同的线程同时运行并且它们彼此等待以释放锁。