什么是“非阻塞”并发,它与普通并发有何不同?

2022-08-31 22:10:15
  1. 什么是“非阻塞”并发,它与使用线程的正常并发有何不同?为什么我们不在需要并发的所有方案中使用非阻塞并发?使用非阻塞并发是否有开销?
  2. 我听说Java中提供了非阻塞并发。是否有任何特定方案应该使用此功能?
  3. 将这些方法之一用于集合是否有区别或优势?权衡取舍是什么?

Q3 示例:

class List   
{  
    private final ArrayList<String> list = new ArrayList<String>();

    void add(String newValue) 
    {
        synchronized (list)
        {
            list.add(newValue);
        }
    }
}  

与。

private final ArrayList<String> list = Collections.synchronizedList(); 

这些问题更多的是从学习/理解的角度出发的。感谢您的关注。


答案 1

什么是非阻塞并发,它有何不同。

正式:

在计算机科学中,非阻塞同步可确保争用共享资源的线程不会因互斥而无限期推迟其执行。如果保证系统范围的进展,非阻塞算法是无锁的;如果每个线程的进度也有保证,则无需等待。(维基百科)

非正式:非阻塞与阻塞的最有利特性之一是,线程不必由操作系统挂起/唤醒。这样的开销可能达到 1ms 到 10ms,因此消除此开销可以带来巨大的性能提升。在java中,这也意味着您可以选择使用非公平锁定,这可能比公平锁定具有更多的系统吞吐量。

我听说这在Java中可用。是否有任何特定的场景我们应该使用此功能

是的,来自Java5。事实上,在Java中,你基本上应该尽可能地尝试使用java.util.concurrent来满足你的需求(这恰好经常使用非阻塞并发,但在大多数情况下你不必明确担心)。只有当您没有其他选择时,才应使用同步包装器(.synchronizedList()等)或手动关键字。这样,您最终大部分时间都可以使用更易于维护、性能更好的应用程序。synchronize

当存在大量争用时,非阻塞并发特别有利。当你需要阻塞(公平锁定,事件驱动的东西,最大长度的队列等)时,你不能使用它,但是如果你不需要它,非阻塞并发在大多数情况下往往表现得更好。

将这些方法之一用于集合是否有区别/优势?权衡取舍

两者具有相同的行为(字节码应相等)。但我建议使用,因为它更短=更小的空间搞砸了!Collections.synchronized


答案 2

什么是非阻塞并发,它与使用线程的普通并发有何不同。

非阻塞并发是协调线程之间的访问与阻塞并发的不同方法。有很多背景(理论)材料,但最简单的解释(似乎你正在寻找一个简单的,动手的答案),是非阻塞并发不使用锁。

我们为什么不在需要并发的所有方案中使用“非阻塞”并发。

我们做到了。我一会儿会告诉你。但是,对于每个并发问题,并不总是有高效的非阻塞算法。

“非阻塞”是否有任何开销

好吧,线程之间任何类型的共享信息都有开销,一直到CPU的结构,特别是当你得到我们所说的“争用”时,即同步多个试图同时写入同一内存位置的线程。但一般来说,在许多情况下,非阻塞比阻塞(基于锁的)并发更快,特别是所有给定算法/数据结构的众所周知的,简单,无锁实现的情况。Java提供的正是这些很好的解决方案。

我听说这在Java中可用。

绝对。对于初学者来说,java.util.concurrent.atomic 中的所有类都提供共享变量的免锁维护。此外,java.util.concurrent 中名称以 ConcurrentLinked 或 ConcurrentSkipList 开头的所有类都提供了列表、映射和集合的无锁实现。

是否有任何特定的场景我们应该使用此功能。

您可能希望在原本(在 JDK 1.5 之前)使用 Collections.synchronizedlist 的所有情况下使用无锁队列和 deque,因为它们在大多数情况下都能提供更好的性能。也就是说,每当多个线程同时修改集合时,或者当一个线程正在修改集合而其他线程正在尝试读取它时,您都可以使用它们。请注意,非常流行的 ConcurrentHashMap 实际上在内部使用锁,但它比 ConcurrentSkipListMap 更受欢迎,因为我认为它在大多数情况下提供了更好的性能。但是,我认为Java 8将包括ConcurrentHashMap的无锁实现。

将这些方法之一用于集合是否有区别/优势?权衡取舍

好吧,在这个简短的例子中,它们是完全相同的。但请注意,当您具有并发读取器和写入器时,必须同步读取和写入,而 Collections.synchronizedList() 可以做到这一点。您可能希望尝试使用无锁 ConcurrentLinkedQueue 作为替代方法。在某些情况下,它可能会为您提供更好的性能。

一般说明

虽然并发性是一个非常重要的学习主题,但请记住,它也是一个非常棘手的主题,即使是非常有经验的开发人员也经常犯错。更糟糕的是,您可能仅在系统负载过重时才发现并发错误。因此,我总是建议使用尽可能多的现成并发类和库,而不是推出自己的并发类和库。


推荐