当前位置: 首页 > 知识库问答 >
问题:

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

诸葛雅达
2023-03-14

我试图理解,如果可以使用synchronized(this),是什么让并发锁如此重要。在下面的伪代码中,我可以执行以下任一操作:

  1. 同步整个方法或同步脆弱区域(synchronized(this){…}

代码:

    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;
}

共有3个答案

华心思
2023-03-14

从oracle留档页面关于Reentry antLock:

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

>

  • ReentrantLock属于上次成功锁定但尚未解锁的线程。当锁不属于另一个线程时,调用锁的线程将返回并成功获取锁。如果当前线程已经拥有锁,该方法将立即返回。

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

    本文介绍的ReentrantLock关键特性

    1. 可中断锁定的能力

    您可以使用ReentrantReadWriteLock。读锁,可重入写锁。WriteLock可进一步控制读写操作的粒度锁定。

    看看Benjamen关于使用不同类型的可重入锁的这篇文章

  • 黄靖
    2023-03-14

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

    您是对的,因为您可以使用同步(this)而不是Reentry antReadWriteLock,但事实并非总是如此。

    如果您想更好地了解Reentry antReadWriteLock的特殊之处,请查找有关生产者-消费者线程同步的一些信息。

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

    顺便说一句,使用同步(this)-通常使用公共类实例进行锁定-可能会有问题,因为它会将您的代码打开到潜在的死锁,因为其他人可能会在不知情的情况下尝试锁定您的对象程序中的其他地方。

    禄烨然
    2023-03-14

    ReentrantLock是非结构化的,不像synchronized构造——也就是说,不需要使用块结构进行锁定,甚至可以跨方法持有锁。举个例子:

    private ReentrantLock lock;
    
    public void foo() {
      ...
      lock.lock();
      ...
    }
    
    public void bar() {
      ...
      lock.unlock();
      ...
    }
    

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

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

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

    ReentrantLock也可能更具可扩展性,在更高的争用下性能更好。你可以在这里了解更多。

    然而,这一说法遭到了质疑;见以下评论:

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

    你什么时候应该使用ReentantLocks?根据开发工作的那篇文章...

    答案很简单——当您实际需要它提供的synchronized不需要的东西时使用它,比如定时锁等待、可中断锁等待、非块结构化锁、多个条件变量或锁轮询ReentrantLock还具有可伸缩性优势,如果您实际遇到高争用的情况,您应该使用它,但请记住,绝大多数synchronized块几乎从不显示任何争用,更不用说高争用了。我建议使用同步进行开发,直到证明同步不足为止,而不是简单地假设如果使用ReentrantLock,那么“性能会更好”。记住,这些是面向高级用户的高级工具。(真正的高级用户倾向于选择他们能找到的最简单的工具,直到他们确信简单的工具是不够的。)和往常一样,先把事情做好,然后再担心你是否必须把事情做得更快。

    在不久的将来,最后一个将变得更加重要的方面与Java15和ProjectLoom有关。在虚拟线程的(新)世界中,底层调度程序使用ReentrantLock比使用synchronized工作得更好,至少在最初的Java 15版本中是这样,但以后可能会进行优化。

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

    如果您有一个由同步保护的通用I/O操作,请将监视器替换为ReentantLock,以便让您的应用程序在我们修复监视器固定之前充分受益于Loom的可扩展性提升(或者,更好的是,如果可以的话,使用更高性能的StampeLock)。

     类似资料:
    • 问题内容: 我试图了解是什么使并发锁如此重要,如果可以使用的话。在下面的虚拟代码中,我可以执行以下任一操作: 同步了整个方法或同步了易受攻击的区域() 或使用ReentrantLock锁定易受攻击的代码区域。 码: 问题答案: 一个ReentrantLock的是非结构化的,不像结构-即你不需要使用块结构锁,甚至可以举行跨越方法的锁。一个例子: 这样的流程不可能通过构造中的单个监视器来表示。 除此之

    • 我想从下面几个角度讨论这个问题: (1)锁的粒度 首先锁的粒度并没有变粗,甚至变得更细了。每当扩容一次,ConcurrentHashMap的并发度就扩大一倍。 (2)Hash冲突 JDK1.7中,ConcurrentHashMap从过二次hash的方式(Segment -> HashEntry)能够快速的找到查找的元素。在1.8中通过链表加红黑树的形式弥补了put、get时的性能差距。 JDK1.

    • 问题内容: Angular对于构建单页应用程序非常有用。它基本上建立在当前范例上,在该范例中我们向不同的端点发出请求,以帮助我们创建更好的单页应用程序。 另一方面,我们拥有的Meteor似乎更加强大,特别是因为它们具有称为DDP的功能,该功能基本上允许双向数据流。看来Meteor是Angular的超集,并且显然是更好的选择。 尽管Angular确实提供了一些诸如测试和本地化之类的功能,但我只是不明

    • 我对Lambda很陌生,以下是教科书(简而言之就是java)所说的: 只有某些类型才有资格成为lambda的目标。目标类型也称为函数接口,它们必须: BE接口 只有一个非默认方法(但可能有其他默认方法) 我知道这个lambda将转换成接口,它是,与书中的描述相符 但是,当希望接收时,我可以在这里使用lambda 但是它有两个非默认方法和。

    • 问题内容: 假设我们的应用程序只有一个线程。然后我们正在使用,这是什么问题? 我的意思是,如果可以通过同步处理多个线程,那么使用单线程有什么问题? 为什么要改用? 问题答案: 是线程安全的,这意味着它们具有同步的方法来控制访问,因此一次只有一个线程可以访问StringBuffer对象的同步代码。因此,在多个线程可能试图同时访问同一StringBuffer对象的多线程环境中,StringBuffer

    • 本文向大家介绍为什么要使用 kafka,为什么要使用消息队列?相关面试题,主要包含被问及为什么要使用 kafka,为什么要使用消息队列?时的应答技巧和注意事项,需要的朋友参考一下 缓冲和削峰:上游数据时有突发流量,下游可能扛不住,或者下游没有足够多的机器来保证冗余,kafka在中间可以起到一个缓冲的作用,把消息暂存在kafka中,下游服务就可以按照自己的节奏进行慢慢处理。 解耦和扩展性:项目开始的