如果可以使用synced(this),为什么使用重入锁?

我试图理解是什么让并发锁定如此重要,如果可以使用.在下面的虚拟代码中,我可以做任何一个:synchronized (this)

  1. 同步整个方法或同步易损区域(synchronized(this){...})
  2. 或者使用重入锁锁定易受攻击的代码区域。

法典:

    private final ReentrantLock lock = new ReentrantLock(); 
    private static List<Integer> ints;

    public Integer getResult(String name) { 
        .
        .
        .
        lock.lock();
        try {
            if (ints.size()==3) {
                ints=null;
                return -9;
            }   

            for (int x=0; x<ints.size(); x++) {
                System.out.println("["+name+"] "+x+"/"+ints.size()+". values >>>>"+ints.get(x));
            }

        } finally {
            lock.unlock();
        } 
        return random;
}

答案 1

与构造不同,ReentrantLock非结构化的 - 即您不需要使用块结构进行锁定,甚至可以跨方法持有锁定。例如:synchronized

private ReentrantLock lock;

public void foo() {
  ...
  lock.lock();
  ...
}

public void bar() {
  ...
  lock.unlock();
  ...
}

这种流不可能通过构造中的单个监视器来表示。synchronized


除此之外,还支持支持超时的锁定轮询可中断锁定等待。 还支持可配置的公平性策略,允许更灵活的线程调度。ReentrantLockReentrantLock

此类的构造函数接受可选的公平性参数。当设置为 ,在争用下时,锁有利于授予对等待时间最长的线程的访问权限。否则,此锁不保证任何特定的访问顺序。与使用默认设置的程序相比,使用公平锁(由许多线程访问)的程序可能显示较低的总体吞吐量(即,速度较慢;通常慢得多),但获得锁和保证没有饥饿的时间差异较小。但请注意,锁的公平性并不能保证线程调度的公平性。因此,使用公平锁的众多线程之一可能会连续多次获得它,而其他活动线程没有前进并且当前未持有该锁。另请注意,不定时方法不遵循公平性设置。如果锁定可用,即使其他线程正在等待,它也会成功。truetryLock


ReentrantLock 也可能更具可扩展性,在更高的争用下表现更好。您可以在此处阅读有关此内容的更多信息。

然而,这一说法受到质疑。请参阅以下注释:

在重入锁定测试中,每次都会创建一个新锁定,因此没有独占锁定,并且生成的数据无效。此外,IBM 链接没有为底层基准测试提供源代码,因此无法确定测试是否正确执行。


什么时候应该使用 s?根据那篇开发者文章...ReentrantLock

答案很简单 - 当你实际需要它提供的东西时使用它,比如定时锁定等待,可中断锁定等待,非块结构锁定,多个条件变量或锁定轮询。 还具有可伸缩性优势,如果您实际遇到表现出高争用的情况,则应使用它,但请记住,绝大多数块几乎从未表现出任何争用,更不用说高争用了。我建议使用同步进行开发,直到同步被证明是不充分的,而不是简单地假设如果使用.of,则“性能会更好”。请记住,这些是高级用户的高级工具。(真正的高级用户往往更喜欢他们能找到的最简单的工具,直到他们确信这些简单的工具是不够的。与往常一样,首先要正确,然后担心您是否必须使其更快。synchronizedReentrantLocksynchronizedReentrantLock


在不久的将来将变得更加相关的最后一个方面与Java 15和Project Loom有关。在虚拟线程的(新)世界中,底层调度程序将能够比它能够更好地工作,这至少在最初的Java 15版本中是正确的,但以后可能会进行优化。ReentrantLocksynchronized

在当前的 Loom 实现中,可以在两种情况下固定虚拟线程:当堆栈上有本机帧时 ( 当 Java 代码调用本机代码 (JNI) 然后回调到 Java 时 — 以及当在块或方法中时。在这些情况下,阻塞虚拟线程将阻塞承载它的物理线程。一旦本机调用完成或监视器被释放(块/方法被退出),线程就会被取消固定。synchronizedsynchronized

如果您有一个由 保护的常见 I/O 操作,请将监视器替换为 ,以便您的应用程序在修复监视器固定之前(或者,如果可以的话,使用更高的性能),就可以让您的应用程序完全受益于 Loom 的可伸缩性提升。synchronizedReentrantLockStampedLock


答案 2

重入ReadWriteLock是一种专用锁,而它是一种通用锁。它们相似但不完全相同。synchronized(this)

你是对的,因为你可以使用而不是,但事实恰恰相反。synchronized(this)ReentrantReadWriteLock

如果您想更好地了解什么使特别查找有关生产者 - 消费者线程同步的一些信息。ReentrantReadWriteLock

通常,您可以记住,可以在大多数应用程序中使用全方法同步和通用同步(使用关键字),而无需过多考虑同步的语义,但是如果您需要从代码中挤出性能,则可能需要探索其他更细粒度或特殊用途的同步机制。synchronized

顺便说一句,使用 - 并且通常使用公共类实例锁定 - 可能会有问题,因为它会将您的代码打开到潜在的死锁,因为其他人可能无意中尝试在程序中的其他位置锁定您的对象。synchronized(this)


推荐