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

cod*_*ver 38 java concurrency

  1. 什么是"非阻塞"并发,它与使用线程的普通并发有什么不同?为什么我们不在所有需要并发的场景中使用非阻塞并发?使用非阻塞并发会有开销吗?
  2. 我听说Java中可以使用非阻塞并发.是否有任何特殊情况我们应该使用此功能?
  3. 使用这些方法中的一种与集合有区别或优势吗?有什么权衡取舍?

Q3的示例:

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

    void add(String newValue) 
    {
        synchronized (list)
        {
            list.add(newValue);
        }
    }
}  
Run Code Online (Sandbox Code Playgroud)

private final ArrayList<String> list = Collections.synchronizedList(); 
Run Code Online (Sandbox Code Playgroud)

问题更多来自学习/理解的观点.谢谢你的关注.

Enn*_*oji 18

什么是非阻塞并发以及它是如何不同的.

正式:

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

【非正式用语】:非阻塞与阻塞最有利的特点之一是,线程不必被操作系统暂停/唤醒.这样的开销可能达到1毫秒到几十毫秒,因此删除它可以获得很大的性能提升.在java中,它还意味着您可以选择使用非公平锁定,这可以比公平锁定具有更多的系统吞吐量.

我听说这是Java提供的.是否有任何特定情况我们应该使用此功能

是的,来自Java5.事实上,在Java中,你应该尽可能地尝试使用java.util.concurrent来满足你的需求(这恰好使用非阻塞并发很多,但在大多数情况下你不必明确担心).只有在没有其他选项时,才应使用synchronized wrappers(.synchronizedList()等)或manual synchronize关键字.这样,大部分时间你都可以使用更易于维护,性能更好的应用程序.

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

使用这些方法之一对集合有区别/优势吗?有什么权衡

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


Bal*_*usC 10

1)什么是非阻塞并发以及它是如何不同的.

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

2)我听说这是Java提供的.是否有任何特定情况我们应该使用此功能

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

3)使用这些方法之一进行收集是否有差异/优势.有什么权衡

我会用Collections#synchronizedList(),不仅因为它更健壮和方便,而且因为a Map不是List.当API已经提供设施时,没有必要尝试在家中增加一个.


也就是说,有一个关于并发Sun教程.我建议你自己完成它.


pro*_*ron 10

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

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

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

我们的确是.我会告诉你一点.但对于每个并发问题,并不总是有效的非阻塞算法.

有没有"非阻塞"的开销

那么,有对任何类型的线程之间共享信息的开销去一路下跌到CPU是如何构成的,尤其是当你得到了什么,我们所说的"争",即同步多个线程试图写同内存位置同时.但一般来说,在许多情况下,非阻塞比阻塞(基于锁定)的并发更快,尤其是在给定算法/数据结构的众所周知的,简单的无锁实现的所有情况下.这些是Java提供的优秀解决方案.

我听说这是Java提供的.

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

是否有任何特定情况我们应该使用此功能.

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

使用这些方法之一对集合有区别/优势吗?有什么权衡

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

一般说明

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


Ed *_*lez 5

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

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

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

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

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

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

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

synchronizedList(或synchronizedMap)对象仅确保各个操作是线程安全的。这意味着两次插入不会同时发生。考虑以下循环:

for(int i=0; i < 4; i++){
    list.add(Integer.toString(i));
}
Run Code Online (Sandbox Code Playgroud)

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

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

但是,如果我们将此列表包装在同步(列表)块中:

synchronized(list){
    for(int i=0; i < 4; i++){
        list.add(Integer.toString(i));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

因此,一些权衡是:

  • 使用syncrhonizedList,您可以在不显式使用同步块的情况下进行插入。
  • SynchronizedList 可能会给您一种错误的安全感,因为您可能天真地认为一个线程上的连续操作是原子的,而实际上只有单个操作是原子的
  • 使用同步(列表)块必须小心,因为我们可能会造成死锁(更多内容见下文)。

当两个(或更多)线程各自等待另一个线程持有的资源子集时,我们可能会产生死锁。例如,如果您有两个列表:userList 和 movieList。

如果线程 1 首先获取 userList 的锁,然后获取 movieList 的锁,但线程 2 执行相反的步骤(先获取 movieList 的锁,然后再获取 userList 的锁),那么我们就陷入了死锁。考虑以下事件过程:

  1. 线程1获得userList的锁
  2. 线程 2 获得 movieList 的锁
  3. 线程 1 尝试获取 movieList 的锁,等待线程 2 释放
  4. 线程 2 尝试获取 userList 的锁,等待线程 1 释放

两个线程都在等待对方,并且都无法继续前进。这是一个阻塞场景,由于双方都不会放弃其资源,因此我们陷入了僵局。