如果可以使用synchronized(this),为什么要使用ReentrantLock?

adh*_*dhg 306 java concurrency multithreading synchronize reentrantlock

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

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

码:

    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;
}
Run Code Online (Sandbox Code Playgroud)

old*_*inb 458

一个ReentrantLock的非结构化的,不像synchronized结构-即你不需要使用块结构锁,甚至可以举行跨越方法的锁.一个例子:

private ReentrantLock lock;

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

public void bar() {
  ...
  lock.unlock();
  ...
}
Run Code Online (Sandbox Code Playgroud)

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


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

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


ReentrantLock 可能也有更多的可扩展性,更高的竞争下进行好得多.你可以在这里阅读更多相关信息.

然而,这一主张受到质疑; 看到以下评论:

在重入锁定测试中,每次都会创建一个新锁,因此没有排他锁定,结果数据无效.此外,IBM链接不提供底层基准测试的源代码,因此无法确定测试是否正确执行.


什么时候应该使用ReentrantLocks?根据那篇developerWorks文章......

答案很简单 - 在实际需要它提供的东西时使用它synchronized,如定时锁等待,可中断锁等待,非块结构锁,多个条件变量或锁定轮询.ReentrantLock还具有可伸缩性的好处,如果你实际上有一个表现出高争用的情况,你应该使用它,但要记住绝大多数synchronized块几乎没有表现出任何争用,更不用说高争用了.我建议使用同步进行开发,直到同步被证明是不合适的,而不是简单地假设"性能会更好",如果你使用的话ReentrantLock.请记住,这些是高级用户的高级工具.(真正的高级用户往往更喜欢他们能找到的最简单的工具,直到他们确信简单的工具不合适.)一如既往,先做好,然后担心你是否必须加快速度.

  • 应删除lycog.com的"已知更具可扩展性"链接.在重入锁定测试中,每次都会创建一个新锁,因此没有排他锁定,结果数据无效.此外,IBM链接不提供底层基准测试的源代码,因此无法确定测试是否正确执行.就个人而言,我只是删除了关于可伸缩性的全部内容,因为整个声明基本上都不受支持. (26认同)
  • 如果性能是您的高度关注,请不要忘记寻找一种您根本不需要同步的方法. (6认同)
  • 不能.给予好评.足够.这里的好消息. (5认同)
  • 我根据您的回复修改了帖子. (2认同)
  • 表演对我完全没有意义。如果可重入锁的性能更好,那为什么不仅仅像可重入锁那样实现同步,又为什么不同步呢? (2认同)
  • @ user2761895 Lycog链接中的ReentrantLockPseudoRandom代码在每次调用setSeed和next之后都使用了全新的,无竞争的锁。 (2认同)

Mik*_*scu 13

ReentrantReadWriteLock是一种专用锁,synchronized(this)而是一种通用锁.它们相似但不完全相同.

你是对的,你可以使用synchronized(this)而不是ReentrantReadWriteLock相反但并非总是如此.

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

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

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

  • 为了防止潜在的死锁,因为其他人不知情可能会在游戏中的其他地方尝试锁定您的对象,请使用私有对象实例作为同步监视器,如下所示:`public class MyLock { private final Object protectedLongLockingMonitor = new Object(); 私人长protectedLong = 0L; public void incrementProtectedLong() { synchronized(protectedLongLockingMonitor) { protectedLong++; } } }` (2认同)

Rav*_*abu 9

从关于ReentrantLock的 oracle文档页面:

可重入互斥锁具有与使用同步方法和语句访问的隐式监视器锁相同的基本行为和语义,但具有扩展功能.

  1. 一个ReentrantLock的由线程拥有最后成功锁定,但尚未解锁的.当锁不是由另一个线程拥有时,线程调用锁将返回,成功获取锁.如果当前线程已拥有锁,则该方法将立即返回.

  2. 此类的构造函数接受可选的fairness参数.当设置为true时,在争用下, 锁定有利于授予对等待时间最长的线程的访问权限.否则,此锁定不保证任何特定的访问顺序.

ReentrantLock根据本文的主要功能

  1. 能够无故锁定.
  2. 能够在等待锁定时超时.
  3. 创造公平锁定的力量.
  4. 用于获取锁定等待线程列表的A​​PI.
  5. 灵活地尝试锁定而不会阻塞.

您可以使用 ReentrantReadWriteLock.ReadLock,ReentrantReadWriteLock.WriteLock进一步获取对读取和写入操作的粒度锁定的控制.

看看Benjamen关于使用不同类型的ReentrantLocks的这篇文章


Sum*_*oor 6

同步锁不提供任何等待队列机制,在该机制中,在一个线程执行后,任何并行运行的线程都可以获得锁。因此,系统中运行较长时间的线程永远没有机会访问共享资源,从而导致饥饿。

可重入锁非常灵活,并且具有公平策略,如果一个线程等待较长时间并且在当前执行线程完成之后,我们可以确保等待时间较长的线程获得访问共享资源的机会,从而减少系统的吞吐量并使其更加耗时。


j2e*_*nue 5

您可以使用具有公平策略或超时的可重入锁来避免线程饥饿。您可以应用线程公平策略。它将有助于避免线程永远等待获取您的资源。

private final ReentrantLock lock = new ReentrantLock(true);
//the param true turns on the fairness policy. 
Run Code Online (Sandbox Code Playgroud)

“公平策略”选择下一个可运行的线程来执行。它基于优先级、自上次运行以来的时间等等

另外,如果 Synchronize 无法逃脱阻塞,它可以无限期地阻塞。可重入锁可以设置超时。