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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2824225/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 13:17:23  来源:igfitidea点击:

What is "non-blocking" concurrency and how is it different than normal concurrency?

javaconcurrency

提问by codeObserver

  1. What is "non-blocking" concurrency and how is it different than normal concurrency using threads? Why don't we use non-blocking concurrency in all the scenarios where concurrency is required? Is there overhead for using non-blocking concurrency?
  2. I have heard that non-blocking concurrency is available in Java. Are there any particular scenarios where we should use this feature?
  3. Is there a difference or advantage to using one of these methods with a collection? What are the trade-offs?
  1. 什么是“非阻塞”并发,它与使用线程的普通并发有何不同?为什么我们不在所有需要并发的场景中使用非阻塞并发?使用非阻塞并发是否有开销?
  2. 我听说非阻塞并发在 Java 中可用。我们应该在哪些特定场景中使用此功能?
  3. 将这些方法之一用于集合是否有区别或优势?有哪些取舍?

Example for Q3:

Q3 示例:

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

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

vs.

对比

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

The questions are more from a learning/understanding point of view. Thanks for attention.

这些问题更多是从学习/理解的角度来看的。谢谢关注。

采纳答案by Enno Shioji

What is Non-blocking Concurrency and how is it different.

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

Formal:

正式的:

In computer science, non-blocking synchronization ensures that threads competing for a shared resource do not have their execution indefinitely postponed by mutual exclusion. A non-blocking algorithm is lock-free if there is guaranteed system-wide progress; wait-free if there is also guaranteed per-thread progress. (wikipedia)

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

Informal: One of the most advantageous feature of non-blocking vs. blocking is that, threads does not have to be suspended/waken up by the OS. Such overhead can amount to 1ms to a few 10ms, so removing this can be a big performance gain. In java, it also means that you can choose to use non-fair locking, which can have much more system throughput than fair-locking.

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

I have heard that this is available in Java. Are there any particular scenarios we should use this feature

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

Yes, from Java5. In fact, in Java you should basically try to meet your needs with java.util.concurrent as much as possible (which happen to use non-blocking concurrency a lot, but you don't have to explicitly worry in most cases). Only if you have no other option, you should use synchronized wrappers (.synchronizedList() etc.) or manual synchronizekeyword. That way, you end up most of the time with more maintainable, better performing apps.

是的,来自 Java5。实际上,在 Java 中,您基本上应该尽量使用 java.util.concurrent 来满足您的需求(碰巧经常使用非阻塞并发,但在大多数情况下您不必明确担心)。仅当您别无选择时,才应使用同步包装器(.synchronizedList() 等)或手动synchronize关键字。这样,您在大多数情况下都会得到更易于维护、性能更好的应用程序。

Non-blocking concurrency is particularly advantageous when there is a lot of contention. You can't use it when you need blocking (fair-locking, event-driven stuff, queue with maximum length etc.), but if you don't need that, non-blocking concurrency tends to perform better in most conditions.

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

Is there a difference/advantage of using one of these methods for a collection. What are the trade offs

使用这些方法之一进行集合是否有区别/优势。什么是权衡

Both have the same behavior (the byte code should be equal). But I suggest to use Collections.synchronizedbecause it's shorter = smaller room to screw up!

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

回答by Lars Andren

1) Non-blocking synchronizationmeans that the risk of deadlocksis removed. No one thread will wait to get a lock held by another thread "forever".

1)非阻塞同步意味着消除了死锁的风险。没有一个线程会“永远”等待获得另一个线程持有的锁。

2) More on non-blocking synchronization algorithmsin java.

2)更多关于java中的非阻塞同步算法

回答by Eric

  1. Wikipedia is a great resource for any Computer Science student. Here is an article on Non-blocking synchronization - http://en.wikipedia.org/wiki/Non-blocking_synchronization

  2. Non-blocking synchronization is available in any language, it is how the programmer defines their multi-threaded application.

  3. You should only use locking (i.e. synchronized (list) ) when necessary due to the time it takes to acquire the lock. In Java, the Vector is a thread safe data structure that is very similar to Java's ArrayList.

  1. 维基百科对任何计算机科学专业的学生来说都是一个很好的资源。这是一篇关于非阻塞同步的文章 - http://en.wikipedia.org/wiki/Non-blocking_synchronization

  2. 任何语言都可以使用非阻塞同步,这是程序员定义多线程应用程序的方式。

  3. 由于获取锁需要时间,因此您应该只在必要时使用锁定(即 synchronized (list) )。在 Java 中,Vector 是一种线程安全的数据结构,与 Java 的 ArrayList 非常相似。

回答by BalusC

1) What is Non-blocking Concurrency and how is it different.

1) 什么是非阻塞并发以及它有何不同。

A task (thread) is non-blocking when it doesn't cause other tasks (threads) to wait until the task is finished.

当一个任务(线程)不会导致其他任务(线程)等待任务完成时,它就是非阻塞的。

2) I have heard that this is available in Java. Are there any particular scenarios we should use this feature

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

Java supports at its own multithreading. You can take benefit of it to run multiple tasks concurrently. When well written and implemented, this may speed up the program when running at a machine with multiple logical CPU's. To start, there are java.lang.Runnableand java.lang.Threadas low level concurrency implementations. Then there is high level concurrency in flavor of the java.util.concurrentAPI.

Java 支持它自己的多线程。您可以利用它同时运行多个任务。如果编写和实施得当,这可能会在具有多个逻辑 CPU 的机器上运行时加速程序。首先,有java.lang.Runnablejava.lang.Thread低级别的并发实现。然后是java.util.concurrentAPI风格的高级并发。

3) Is there a difference/advantage of using one of these methods for a collection. What are the trade offs

3)使用这些方法之一进行集合是否有区别/优势。什么是权衡

I would use Collections#synchronizedList(), not only because that's more robust and convenient, but also because a Mapisn't a List. There's no need to attempt to homegrow one when the API already offers the facility.

我会使用Collections#synchronizedList(),不仅因为它更健壮和方便,还因为 aMap不是 a List。当 API 已提供该功能时,无需尝试自行开发。



That said, there's a Sun tutorial about Concurrency. I recommend you to get yourself through it.

也就是说,有一个关于 ConcurrencySun 教程。我建议你自己通过它。

回答by Ed Gonzalez

1]What is Non-blocking Concurrency and how is it different.

1]什么是非阻塞并发以及它有何不同。

As others have mentioned, Non-blocking is a way of saying deadlock-free (meaning we shouldn't have a condition where progress halts entirely while threads are blocked, waiting for access).

正如其他人所提到的,非阻塞是一种无死锁的说法(这意味着我们不应该遇到在线程被阻塞等待访问时进度完全停止的情况)。

What is meant by 'concurrency' is just that multiple computations are happening at the same time (concurrently).

“并发”的意思只是多个计算同时(并发)发生。

2] I have heard that this is available in Java. Are there any particular scenarios we should use this feature

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

You want to use non-blocking algorithms when it is important that multiple threads can access the same resources concurrently, but we aren't as concerned with the order of access or the possible ramifications of interleaving action (more on this below).

当多个线程可以同时访问相同资源很重要时,您希望使用非阻塞算法,但我们并不关心访问顺序或交错操作的可能后果(更多内容见下文)。

3] Is there a difference/advantage of using one of these methods for a collection. What are the trade offs

3] 使用这些方法之一进行集合是否有区别/优势。什么是权衡

.

.

Using the synchronized(list) block ensures that all of the actions performed within the block are seen as atomic. That is to say, as long as we only access the list from synchronized(list) blocks, all updates to the list will appear as if they happened at the same time within the block.

使用 synchronized(list) 块可确保块内执行的所有操作都被视为原子操作。也就是说,只要我们只从synchronized(list) 块中访问列表,对列表的所有更新都会看起来好像它们是在块内同时发生的。

A synchronizedList (or synchronizedMap) object only ensures that individual operations are thread-safe. This means that two inserts will not occur concurrently. Consider the following loop:

同步列表(或同步映射)对象仅确保单个操作是线程安全的。这意味着两个插入不会同时发生。考虑以下循环:

for(int i=0; i < 4; i++){
    list.add(Integer.toString(i));
}

If the list in use was a synchronizedList and this loop was executed on two different threads, then we may end up with {0,0,1,2,1,3,2,3} in our list, or some other permutation.

如果使用的列表是一个 synchronizedList 并且这个循环是在两个不同的线程上执行的,那么我们最终可能会在我们的列表中得到 {0,0,1,2,1,3,2,3} 或其他一些排列。

Why? Well, we are guaranteed that thread 1 will add 0-3 in that order and we are guaranteed the same of thread 2, however we have no guarantee of how they will interleave.

为什么?好吧,我们保证线程 1 将按该顺序添加 0-3,并且我们保证与线程 2 相同,但是我们不能保证它们将如何交错。

If, however, we wrapped this list in a synchronized(list) block:

但是,如果我们将此列表包装在一个 synchronized(list) 块中:

synchronized(list){
    for(int i=0; i < 4; i++){
        list.add(Integer.toString(i));
    }
}

We are guaranteed that the inserts from thread 1 and thread 2 will not interleave, but they will occur all at once. Our list will contain {0,1,2,3,0,1,2,3}. The other thread will block, waiting on list, until the first thread completes. We have no guarantee which thread will be first, but we are guaranteed it will finish before the other begins.

我们保证来自线程 1 和线程 2 的插入不会交错,但它们会同时发生。我们的列表将包含 {0,1,2,3,0,1,2,3}。另一个线程将阻塞,等待列表,直到第一个线程完成。我们不能保证哪个线程会先执行,但我们保证它会在另一个线程开始之前完成。

So, some trade-offs are:

因此,一些权衡是:

  • With a syncrhonizedList, you can insert without explicitly using a synchronized block.
  • A syncrhonizedList might give you a false sense of security, since you may naively believe successive opertaions on one thread to be atomic, when only individual operations are atomic
  • Using a syncrhonized(list) block must be done with care, because we are in a position to create deadlock (more below).
  • 使用 syncrhonizedList,您可以在不显式使用同步块的情况下插入。
  • syncrhonizedList 可能会给您一种错误的安全感,因为您可能天真地认为一个线程上的连续操作是原子的,而只有单个操作是原子的
  • 使用同步(列表)块必须小心,因为我们有可能造成死锁(更多内容见下文)。

We can create a deadlock when two (or more) threads are each waiting for a subset of resources held by another. If, for example, you had two lists: userList and movieList.

当两个(或多个)线程都在等待另一个线程持有的资源子集时,我们可以创建死锁。例如,如果您有两个列表:userList 和 movieList。

If thread 1 first acquires the lock to userList, then movieList, but thread two performs these steps in reverse (acquires the lock to movieList before userList), then we have opened ourself up for deadlock. Consider the following course of events:

如果线程 1 首先获取 userList 的锁,然后是 movieList,但线程 2 执行这些步骤相反(在 userList 之前获取到 movieList 的锁),那么我们已经打开了自己的死锁。考虑以下事件过程:

  1. Thread 1 gets lock to userList
  2. Thread 2 gets lock to movieList
  3. Thread 1 tries to get lock to movieList, waits on Thread 2 to release
  4. Thread 2 tries to get lock to userList, waits on Thread 1 to release
  1. 线程 1 锁定 userList
  2. 线程 2 锁定到 movieList
  3. 线程 1 尝试锁定电影列表,等待线程 2 释放
  4. 线程 2 尝试锁定 userList,等待线程 1 释放

Both threads are waiting for the other and neither can move forward. This is a blocking scenario, and since neither will relinquish its resource, we are deadlocked.

两个线程都在等待另一个线程,并且都不能继续前进。这是一个阻塞场景,因为两者都不会放弃它的资源,所以我们陷入了僵局。

回答by pron

What is Non-blocking Concurrency and how is it different than Normal Concurrency using Threads.

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

Non-blocking concurrency is a different way to coordinate access between threads from blocking concurrency. There is a lot of background (theoretical) material out there, but the simplest explanation (as it seems that you're looking for a simple, hands-on answer), is that non-blocking concurrency does not make use of locks.

非阻塞并发是一种与阻塞并发协调线程间访问的不同方式。那里有很多背景(理论)材料,但最简单的解释(似乎您正在寻找一个简单的、实际操作的答案)是非阻塞并发不使用锁。

Why dont we use "non-blocking" Concurrency in all the scenarios where concurrency is required.

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

We do. I'll show you in a bit. But it is true that there aren't always efficient non-blocking algorithms for every concurrency problem.

我们的确是。我一会儿给你看。但确实,对于每个并发问题,并不总是有高效的非阻塞算法。

are there any overheads for "non-blocking"

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

Well, there's overhead for any type of sharing information between threads that goes all the way down to how the CPU is structured, especially when you get what we call "contention", i.e. synchronizing more than one thread that are attempting to write to the same memory location at the same time. But in general, non-blocking is faster than blocking (lock-based) concurrency in many cases, especially all the cases where there are well known, simple, lock-free implementation of a given algorithm/data-structure. It is these good solutions that are provided with Java.

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

I have heard that this is available in Java.

我听说这在 Java 中可用。

Absolutely. For starters, all the classes in java.util.concurrent.atomic provide lock-free maintenance of shared variables. In addition, all the classes in java.util.concurrent whose names start with ConcurrentLinked or ConcurrentSkipList, provide lock-free implementation of lists, maps and sets.

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

Are there any particular scenarios we should use this feature.

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

You would want to use the lock-free queue and deque in all cases where you would otherwise (prior to JDK 1.5) use Collections.synchronizedlist, as they provide better performance under most conditions. i.e., you would use them whenever more than one thread is concurrently modifying the collection, or when one thread is modifying the collection and other threads are attempting to read it. Note that the very popular ConcurrentHashMap does actually use locks internally, but it is more popular than ConcurrentSkipListMap because I think it provides better performance in most scenarios. However, I think that Java 8 would include a lock-free implementation of ConcurrentHashMap.

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

Is there a difference/advantage of using one of these methods for a collection. What are the trade offs

使用这些方法之一进行集合是否有区别/优势。什么是权衡

Well, in this short example, they are exactly the same. Note, however, that when you have concurrent readers and writers, you must synchronize the reads as well as the writes, and Collections.synchronizedList() does that. You might want to try the lock-free ConcurrentLinkedQueue as an alternative. It might give you better performance in some scenarios.

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

General Note

一般说明

While concurrency is a very important topic to learn, bear in mind that it is also a very tricky subject, where even very experienced developers often err. What's worse, you might discover concurrency bugs only when your system is under heavy load. So I would always recommend using as many ready-made concurrent classes and libraries as possible rather than rolling out your own.

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

回答by Scott Taylor

1: What is "non-blocking" concurrency and how is it different than normal concurrency using threads? Why don't we use non-blocking concurrency in all the scenarios where concurrency is required? Is there overhead for using non-blocking concurrency?

1:什么是“非阻塞”并发,它与使用线程的普通并发有何不同?为什么我们不在所有需要并发的场景中使用非阻塞并发?使用非阻塞并发是否有开销?

Non blocking algorithms do not use specific object locking schemes to control concurrent access to memory (synchronized and standard object locks are examples that use object/function level locks to reduce concurrent access problems in Java. Instead these use some form of low level instruction to perform (on some level) a simulataneous compare and swap on a memory location; if this fails it just returns false and does not error out, if it works then it was successful and you move on. Generally, this is attempted in a loop until it works, since there will only be small periods of time (hopefully) when this would fail, it just loops a few extra times until it can set the memory it needs to.

非阻塞算法不使用特定的对象锁定方案来控制对内存的并发访问(同步和标准对象锁是使用对象/函数级锁来减少 Java 中的并发访问问题的示例。而是使用某种形式的低级指令来执行(在某种程度上)在内存位置上同时进行比较和交换;如果失败,它只会返回 false 并且不会出错,如果它有效,那么它成功并且您继续前进。通常,这是在循环中尝试,直到它工作,因为只有一小段时间(希望如此)会失败,它只会循环几次,直到它可以设置它需要的内存。

This is not always used because it is much more complex from a code perspective even for relatively trivial use cases than the standard Java synchronization. Moreover, for most uses the performance impact of the locking is trivial compared to other sources in the system. In most cases, the performance requirements are not nearly high enough to warrant even looking at this.

这并不总是使用,因为从代码的角度来看,即使对于相对简单的用例,它也比标准 Java 同步复杂得多。此外,对于大多数用途,与系统中的其他来源相比,锁定对性能的影响是微不足道的。在大多数情况下,性能要求几乎没有高到足以保证甚至查看这一点。

Finally, as the JDK/JRE evolves, the core designers are improving the internal language implementations to attempt to incorporate the most efficient means of achieving these ends in the core constructs. As you move away from the core constructs, you lose the automatic implementation of those improvements since you are using less standard implementations (for instance jaxb/jibx; jaxb used to grossly underperform jibx, but now is equal if not faster in most cases that I've tested as of java 7) when you bump your java version.

最后,随着 JDK/JRE 的发展,核心设计者正在改进内部语言实现,以尝试将实现这些目标的最有效方法合并到核心构造中。当您远离核心构造时,您将失去这些改进的自动实现,因为您使用的标准实现较少(例如 jaxb/jibx;jaxb 过去的性能远远低于 jibx,但现在在大多数情况下即使不是更快,也与我相同已经从 Java 7 开始测试),当你撞到你的 Java 版本时。

if you look at the code example below, you can see the 'overhead' locations. It's not really overhead per se, but the code must be extremely efficient in order to work non-locking and actually perform better than a standard synchronized version due to the looping. Even slight modifications can lead to code that will go from performing several times better than standard to code that is several times worse (for instance object instantiations that don't need to be there or even quick conditional checks; you're talking about saving cycles here, so the difference between success and failure is very slim).

如果您查看下面的代码示例,您可以看到“开销”位置。它本身并不是真正的开销,但代码必须非常高效才能非锁定工作,并且由于循环而实际上比标准同步版本执行得更好。即使是轻微的修改也会导致代码的性能从比标准好几倍,变成差几倍的代码(例如,不需要存在的对象实例化,甚至快速的条件检查;你说的是节省周期在这里,所以成功和失败之间的区别非常小)。

2: I have heard that non-blocking concurrency is available in Java. Are there any particular scenarios where we should use this feature?

2:我听说Java中可以使用非阻塞并发。我们应该在哪些特定场景中使用此功能?

In my opinion you should only use this if you A) have a proven performance problem in your running system in production, on its production hardware; and B) if you can prove that the only inefficiency left in the critical section is locking related; C) you have firm buy in from your stakeholders that they are willing to have non-standard less maintainable code in return for the performance improvement which you must D) prove numerically on your production hardware to be certain it will even help at all.

在我看来,只有在以下情况下才应该使用它: A) 在生产中的运行系统中,在其生产硬件上存在经证实的性能问题;B) 如果你能证明临界区中唯一的低效率与锁定有关;C)您从利益相关者那里获得了坚定的认同,他们愿意使用非标准的不易维护的代码来换取性能改进,您必须这样做 D)在您的生产硬件上以数字方式证明它甚至会有所帮助。

3: Is there a difference or advantage to using one of these methods with a collection? What are the trade-offs?

3:将这些方法之一用于集合是否有区别或优势?有哪些取舍?

The advantage is performance, the trade off is first that it's more specialized code (so many developers don't know what to make of it;making it harder for a new team or new hire to come up to speed, remember that the majority of the cost of software is labor, so you have to watch the total cost of ownership that you impose through design decisions), and that any modifications should be tested again to ensure that the construct is still actually faster. Generally in a system that would require this some performance or load and throughput testing would be required for any changes. If you aren't doing these tests then I would argue that you almost certainly don't need to even think about these approaches, and would almost definitely not see any value for the increased complexity (if you got it all to work right).

优点是性能,权衡首先是它是更专业的代码(所以很多开发人员不知道该怎么做;让新团队或新员工更难跟上进度,请记住,大多数软件的成本是劳动力,因此您必须注意通过设计决策强加的总拥有成本),并且应该再次测试任何修改以确保构造实际上仍然更快。通常,在需要此功能的系统中,任何更改都需要进行一些性能或负载和吞吐量测试。如果您不进行这些测试,那么我认为您几乎肯定不需要考虑这些方法,并且几乎肯定不会看到增加的复杂性有任何价值(如果一切正常)。

Again, I just have to restate all the standard warnings against optimization generally, as many of these arguments are the same that I would use against this as a design. Many of the drawbacks to this are the same as any optimization, for instance, whenever you change the code you have to ensure that your 'fix' doesn't introduce inefficiency in some construct that was only placed there to improve performance, and deal with that (meaning up to refactoring the entire section to potentially remove the optimizations) if the fix is critical and it reduces performance.

同样,我只需要重申所有针对优化的标准警告,因为这些论点中的许多与我在设计中使用的相同。对此的许多缺点与任何优化相同,例如,每当您更改代码时,您都必须确保您的“修复”不会在某些仅用于提高性能的构造中引入低效率,并处理如果修复很关键并且会降低性能,那(意味着重构整个部分以可能删除优化)。

It is really, really easy to mess this up in ways that are very difficult to debug, so if you don't have to do it (which I have only found a few scenarios where you ever would; and to me those were pretty questionable and I would have preferred to not do it) do not do it. use the standard stuff and everyone will be happier!

以非常难以调试的方式将其搞砸真的非常非常容易,所以如果您不必这样做(我只发现了一些您曾经会这样做的场景;而对我来说这些是非常值得怀疑的我宁愿不这样做)不要这样做。使用标准的东西,每个人都会更快乐!

Discussion/Code

讨论/代码

Non blocking or lock free concurrency avoids the use of specific object locks to control shared memory access (like synchronized blocks or specific locks). There is a performance advantage when the code section is non-locking; however, the code in the CAS loop(if this is the way you go, there are other methods in Java) must be very, very efficient or this will end up costing you more performance than you gain.

非阻塞或无锁并发避免使用特定对象锁来控制共享内存访问(如同步块或特定锁)。当代码段为非锁定时,有性能优势;但是,CAS 循环中的代码(如果这是您要走的路,Java 中还有其他方法)必须非常非常高效,否则最终会消耗更多的性能。

Like all performance optimizations, the extra complexity is not worth the effect for most use cases. Cleanly written Java using standard constructs will work as well if not better than most optimizations (and actually allow your organization to maintain the software more easily once you're gone). To my mind this only makes sense in very high performance sections with proven performance issues where the locking is the only source of inefficiency. If you do not definitely have a known and quantified performance problem, I would avoid the use of any technique like this until you have proven the problem is actually there because of the locking and not do to other issues with the efficiency of the code. Once you have a proven locking based performance problem I would ensure that you have some type of metric in place to ensure that this type of setup is actually going to run faster for you than just using standard Java concurrency.

与所有性能优化一样,对于大多数用例而言,额外的复杂性不值得。如果不比大多数优化更好,使用标准结构编写干净的 Java 也能正常工作(并且实际上允许您的组织在您离开后更轻松地维护软件)。在我看来,这仅在具有已证明的性能问题的非常高性能的部分中才有意义,其中锁定是低效率的唯一来源。如果您没有明确的已知和量化的性能问题,我会避免使用任何类似的技术,直到您证明问题确实存在,因为锁定而不是代码效率的其他问题。

The implementation that I have done for this use CAS operations and the Atomic family of variables. This basic code has never locked up or raised any errors for me in this use case (random sampling input and output for offline testing from a high throughput translation system). It basically works like this:

我为此所做的实现使用了 CAS 操作和 Atomic 系列变量。在这个用例中,这个基本代码从来没有锁定或引发任何错误(随机采样输入和输出用于来自高吞吐量翻译系统的离线测试)。它基本上是这样工作的:

You have some object that is shared between threads, and this is declared as either an AtomicXXX or an AtomicReference (for most non-trivial use cases you'll run with the AtomicReference version).

您有一些在线程之间共享的对象,它被声明为 AtomicXXX 或 AtomicReference(对于大多数非平凡用例,您将使用 AtomicReference 版本运行)。

when the given value/object is referenced, you retrieve it from the Atomic wrapper, this gets you a local copy on which you perform some modification. From here you use a compareAndSwap as the condition of a while loop to attempt to set this Atomic from your thread, if this fails it returns false as opposed to locking up. This will iterate until it works (the code in this loop must be very efficient and simple).

当给定的值/对象被引用时,您从 Atomic 包装器中检索它,这会为您提供一个本地副本,您可以在其中执行一些修改。从这里开始,您使用 compareAndSwap 作为 while 循环的条件来尝试从您的线程设置此 Atomic,如果失败,则返回 false 而不是锁定。这将迭代直到它工作(这个循环中的代码必须非常有效和简单)。

You can look up CAS operations to see how they work, it's basically intended to be implemented as a single instruction set with a comparison at the end to see if the value is what you tried to set it to.

您可以查找 CAS 操作以了解它们是如何工作的,它基本上旨在作为单个指令集实现,并在末尾进行比较以查看该值是否是您尝试将其设置为的值。

If the compareAndSwap fails, you get your object again from the Atomic wrapper, perform any modifications again, and then try the compare and swap again until it works. There is no specific lock, you're just trying to set the object back into memory and if it fails you just try again whenever your thread gets control again.

如果 compareAndSwap 失败,您将再次从 Atomic 包装器中获取您的对象,再次执行任何修改,然后再次尝试比较和交换,直到它起作用为止。没有特定的锁,您只是尝试将对象设置回内存,如果失败,您只需在线程再次获得控制权时再试一次。

Code for this is below for a simple case with a List:

下面是一个带有列表的简单案例的代码:

/* field declaration*/
//Note that I have an initialization block which ensures that the object in this
//reference is never null, this was required to remove null checks and ensure the CAS
//loop was efficient enough to improve performance in my use case
private AtomicReference<List<SampleRuleMessage>> specialSamplingRulesAtomic = new AtomicReference<List<SampleRuleMessage>>();


/*start of interesting code section*/

    List<SampleRuleMessage> list = specialSamplingRulesAtomic.get();
    list.add(message);
    while(!specialSamplingRulesAtomic.compareAndSet(specialSamplingRulesAtomic.get(), list)){
        list = specialSamplingRulesAtomic.get();
        list.add(message);
    };
 /* end of interesting code section*/

回答by bill schwarz

Non-blocking synchronization is the same a blocking synchronization, both are kind of synchronization, the only difference is that non-blocking synchronization is faster overall.

非阻塞同步与阻塞同步是一样的,都是一种同步,唯一的区别是非阻塞同步总体上更快。

For starters you want to use synchronization only when multiple threads access the same resource in RAM. You can't use synchronization when trying to access things on disk, or better said, you need to use locks on disk.

首先,您只想在多个线程访问 RAM 中的相同资源时才使用同步。尝试访问磁盘上的内容时不能使用同步,或者更好地说,您需要在磁盘上使用锁。

That said, how can you synchronize if no thread ever blocks?

也就是说,如果没有线程阻塞,你如何同步?

The answer is optimistic locking. This idea has existed for at least 20 years. Maybe more.

答案是乐观锁。这个想法已经存在了至少 20 年。也许更多。

You maybe have heard of the Lisp language. As it turns out functional languages never modify its parameters, only return new values, so they never need to synchronize.

您可能听说过 Lisp 语言。事实证明,函数式语言从不修改其参数,只返回新值,因此它们永远不需要同步。

In Lisp you can have shared state, but it gets tricky. So most programs can run in parallel and never worry about synchronization.

在 Lisp 中,您可以共享状态,但它变得棘手。所以大多数程序可以并行运行,不用担心同步。

The idea of optimistic locking is that all threads modify shared valued willingly, but they have a local area to modify the values, and only apply the modification at the end, with one instruction, atomically, using CAS. Cas stands for Compare And Swap,it performs in just one CPU cycle, and it has been implemented in CPUs for at least 20 years.

乐观锁的想法是所有线程都愿意修改共享值,但它们有一个局部区域来修改值,并且只在最后应用修改,使用 CAS 原子地使用一条指令。Cas 代表 Compare And Swap,它只在一个 CPU 周期内执行,并且它已经在 CPU 中实现了至少 20 年。

An excellent explanation of what CAS is: https://www.cs.umd.edu/class/fall2010/cmsc433/lectures/nonBlocking.pdf

CAS 是什么的一个很好的解释:https: //www.cs.umd.edu/class/fall2010/cmsc433/lectures/nonBlocking.pdf

So if there is a conflict in the modification, it will only affect one of the writers, the rest will be done with it.

所以如果修改有冲突,只会影响其中一个作者,其他的就自己处理。

Moreover, if there is no contention whatsoever, non-blocking algorithms perform a lot faster than their blocking counterparts.

此外,如果没有任何争用,非阻塞算法的执行速度要比阻塞算法快得多。

Tutorial on non-blocking algorithms in Java with code examples you can use in real life: http://tutorials.jenkov.com/java-concurrency/non-blocking-algorithms.html

Java 中的非阻塞算法教程以及您可以在现实生活中使用的代码示例:http: //tutorials.jenkov.com/java-concurrency/non-blocking-algorithms.html