1、解释为什么静止一致性是可组合的。
组合性与静止一致性
组合性是指当系统中的每个对象都满足属性 $ P $ 时,整个系统也满足 $ P $。
在大型系统中,组合性非常重要。由于系统的复杂性,通常需要采用模块化的设计和实现方式,各个组件可以独立地进行设计、实现以及验证其正确性。
对于 静止一致性 而言,可以将独立实现的静止一致对象组合起来,构建更为复杂的静止一致对象。因此, 静止一致性是可组合的 。
2、考虑以下一种相当不寻常的方法 m 的实现。在每个调用历史中,一个线程第 i 次调用 m 时,该调用会在 2i 步之后返回。这个方法是无等待的、有界无等待的,还是都不是?
有界无等待的
3、证明顺序一致性是非阻塞的。
顺序一致性和静止一致性一样,任何对全方法的待处理调用总能完成,因此顺序一致性是非阻塞的。
4、提出一种方法来修复通用构造,使其能在有限的内存下工作,即使用有限数量的共识对象和有限数量的读写寄存器。
添加一个“before”字段到节点,并在代码中构建一个内存回收方案。
5、有一种CLHLock的实现,其中线程重用自己的节点而非前驱节点。解释这种实现可能出现的问题。
在这种实现中,存在以下问题:
-
AtomicReference<Qnode> tail = new Qnode();初始化错误,AtomicReference构造函数应传入null而非Qnode实例。 -
ThreadLocal<Qnode> myNode未初始化,调用myNode.get()会得到null,后续操作会引发NullPointerException。 -
解锁时只将当前节点的
locked字段设为false,未正确处理节点重用逻辑,可能导致后续锁操作异常。此外,在垃圾回收语言中虽不强制节点重用,但在C++或C等语言中,这种实现若不妥善处理节点回收,会造成内存泄漏。
6、假设有n个线程,每个线程先执行foo()方法,再执行bar()方法。我们希望确保所有线程完成foo()后,才开始执行bar()。为此,我们在foo()和bar()之间设置一个屏障。第一种屏障实现方式:使用一个由测试 - 测试 - 设置锁保护的计数器。每个线程锁定计数器,将其加1,释放锁,然后循环读取计数器,直到其值达到n。第二种屏障实现方式:使用一个n元素数组b[0..n - 1],初始值都为0。线程0将b[0]设为1。对于0 < i < n - 1的每个线程i,循环等待直到b[i - 1]为1,将b[i]设为1,然后等待b[i + 1]变为2,之后离开屏障。线程n - 1检测到b[n - 2]为1后,将b[n - 1]设为2并离开屏障。比较这两种实现在基于总线的缓存一致性架构上的表现。解释在低负载和高负载情况下,哪种方法性能更好。
在低负载时,第二种实现性能更好。因为数组元素独立,线程等待局部数据变化,减少总线竞争和缓存一致性开销。
在高负载时,第一种实现性能更好。因为高负载下第二种实现的数组元素更新同步开销大,而第一种的计数器虽有锁竞争,但操作简单。
7、解释为什么细粒度列表的 add() 方法是可线性化的。
通常证明并发对象实现可线性化的方法是为每个方法确定一个线性化点,在该点方法生效。对于基于锁的实现,每个方法的临界区可作为其线性化点;对于无锁实现,线性化点通常是方法调用的效果对其他方法调用可见的单一步骤。
细粒度列表的 add() 方法中,若线程成功锁定并验证 find() 结果直至新节点的 topLevel , add() 调用将成功,因为线程持有所需的所有锁。之后线程分配一个具有适当键和随机选择的 topLevel 的新节点,将其链接进去,并设置新节点的 fullyLinked 标志。设置该标志是成功 add() 方法的线性化点,所以该方法是可线性化的。
8、请说明在乐观算法中,一个线程永远尝试删除一个节点的场景。
- 如果不断有新节点被添加和删除,一个尝试删除节点

最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



