维基百科说:
非阻塞算法确保了这一点 线程竞争共享 资源没有执行 无限期地被相互推迟 排斥。
当多个线程需要访问某个关键部分时,如何确保实现这一目标?
答案 0 :(得分:2)
请原谅这篇文章,但这往往是我的写作风格。
非阻塞算法有各种各样的形状,但基本思想是构造算法,使得您以一个或多或少的确定性步骤完成,而不是通过锁无限期地停止。如果您的操作可能非常耗时,有时您可以做的最好的事情就是确保您的线程有其他工作来推动程序的整体操作。
并非所有算法都适用于非阻塞方法,并且许多非阻塞方法在其中具有小的关键部分,因此严格来说不是非阻塞的,但在实践中表现如此。在实现任何类型的并发时使用你的头,因为无论你是否是一位经验丰富的程序员,它都是调试的负担。
- =非阻塞算法= -
原子算法执行在无法中断的单个步骤中发生的更新。一个简单的例子是使用++或+ =递增一个值,它在更新发生时简单地归结为单个CPU指令,因此将完成而不会被中断,并且如果多处理中断,CPU将修复它更新。我不太确定这会扩展到SIMD指令的范围,其中一条CPU指令接触多个数据。
如果您有一个不需要返回值的算法,则可能使用生产者/消费者队列系统,其中只有队列是基于锁的,或者可能使用原子更新将项插入队列。在任何一种情况下,队列都会快速更新,并在消费者线程处理积压时将控制权返回给调用者。网络和磁盘写入通常是这种类型。
如果您需要返回值,则回调可以在操作完成时通知您的线程,或者您可以处理一些新数据。最理想的是,您还有其他事情要做,而不是简单地等待回调。
CAS算法,确保在第一次到完成获胜系统上进行读取和更新是另一种确保进度的方法,但是中止需要时间,因此如果算法需要重试,则可以创建非确定性完成时间中止了。数据库使用一种CAS进行事务完成,称为乐观锁定。当争用率较低时,或者在争用需要通知用户并收集额外输入的情况下,这种方法效果最佳。
- =阻止算法= -
阻止算法阻止除了尝试在共享资源上操作的单个或少量多个线程之外的所有线程的进度。如果操作很长,这可能会导致严重的延迟,因此任何阻塞算法的目标应该是将争用发生的关键部分减少到尽可能小的整体算法的一小部分。执行此操作的数据库事务使用悲观锁定。
- =死锁和活锁= -
死锁是两个线程彼此阻塞的地方,通常是因为每个线程都持有另一个想要的锁。
实时锁定可能发生在两个线程再次想要访问其他控件的资源的情况下,或者两个线程完全由彼此发送的数据消耗的情况。在任何一种情况下,算法都会不停地尝试前进,但两个(或可能更多)线程会继续旋转而不会实现真正的前进。
- =重要性= -
为什么这一点很重要?任何类型的并发性的主要问题是可能出现不可预测的状态,因为两个线程在相同的数据上运行而不考虑彼此的变化。为了防止这种情况,使用阻塞和非阻塞算法,但如果出错,最终可能会出现死锁,实时锁或数据损坏。
我希望这强调BOTH阻塞和非阻塞算法存在并发问题,并且无论你选择何种方法杀死并发攻击,你都必须密切关注程序的结构使用方式或提供共享资源。
答案 1 :(得分:1)
非阻塞算法利用CAS:Java Concurrency: CAS vs Locking