我只是想知道为什么非阻塞并发比阻塞并发更好。阻止并发您的线程必须等到其他线程完成其执行。因此线程在这种情况下不会消耗CPU。
但是,如果我谈论非阻塞并发,线程不等待获取锁定,如果某些线程包含锁定,它们会立即返回。
对于ConcurrentHashMap
类中的示例,在put()
方法内,循环中有tryLock()
。其他线程将处于活动状态并不断尝试检查是否已释放锁定,因为tryLock()
为非阻止。我假设在这种情况下,CPU是不必要的。
那么暂停线程直到其他线程完成执行并在工作结束时唤醒线程是不是很好?
答案 0 :(得分:1)
阻塞或非阻塞并发性是否更好取决于您期望等待获取您正在等待的资源的时间长度。
使用阻塞等待(即互斥锁锁定,在C语言中),操作系统内核将等待线程置于休眠状态。在 所需资源可用之后,CPU调度程序将不会为其分配任何时间。这里的优点是,正如你所说的,这个线程在休眠时不会消耗任何CPU资源。
然而,有一个缺点:将线程置于休眠状态,确定何时应该被唤醒以及再次唤醒它的过程是复杂且昂贵的,并且可能否定通过不让线程消耗CPU而实现的节省等候。此外(也可能是因为这个),操作系统可能会选择 not ,一旦资源可用就立即唤醒线程,因此锁定可能会等待的时间超过必要的时间。
非阻塞等待(也称为 spinlock )会在等待时消耗CPU资源,但会节省将线程置于睡眠状态,确定何时应该被唤醒以及唤醒它的费用。一旦锁定空闲,它也可能能够更快地响应,因为在可以继续执行的时候,操作系统的兴趣就会减少。
因此,作为一般规则,如果您希望只等待一小段时间(例如,另一个线程可能需要几个CPU周期来完成ConcurrentHashMap
中的条目),您应该更喜欢自旋锁。对于更长的等待(例如,在同步I / O上,或等待单个复杂计算的多个线程),可能更喜欢互斥(阻塞等待)。
答案 1 :(得分:0)
如果您考虑将ConcurrentHashMap作为示例,考虑由于多个线程执行更新操作(如put)而导致的开销,并阻止等待锁释放(因为您提到其他线程将处于活动状态并不断尝试检查是否锁定已经被释放了,总是不会如此。
与HashTable相比,ConcurrentHashMap中的并发控制被拆分。因此,多个线程可以获取锁定(在表的段上)。
最初,ConcurrentHashMap类支持硬连线的预设并发级别32.这允许最多32个put和/或remove操作同时进行(当同时尝试超过32个线程时,同步以外的因素往往是瓶颈更新。)
此外,使用get(key)和containsKey(key)成功检索(当密钥存在时)通常无锁定地运行。
因此,例如,一个线程可能正在添加一个元素,使用这种锁定策略无法完成的操作是仅在元素尚未存在时添加元素(ConcurrentReaderHashMap提供此类工具)。
此外,size()和isEmpty()方法需要跨32个控制段进行累积,因此可能稍微慢一些。