我知道编写非阻塞(无锁)程序真的很难。仍然是在使用基本构建块(即CAS操作)在Java中编写一个时必须注意的所有事情。
- 编辑 -
如果我有共享资源而我不想锁定共享资源,而是希望在while循环中使用compareAndSet(expectedValue,valueToSet)指令,直到我成功为止。在java中,我们有支持此操作的AtomicXXX类。例如,编写一个非阻塞堆栈参考(Java Concurrency in Practice)
我需要记住的所有事情,所有测试场景都可以在那里......
答案 0 :(得分:3)
无锁编程的主要困难在于架构层面。您需要以某种方式设计代码和算法,以免它们干扰彼此的数据。您需要在单独的任务中“阻止”您的代码,这些任务可以单独运行,尤其是独立运行。然而,一旦你深入研究这些想法,你就开始想知道为什么你之前使用过这么多的阻塞。
示例:给定是一个句子数组,您想要计算每个句子中的单词,并且您希望以线程方式进行计算。
首先,您需要精确定位“阻塞点”,这是线程可能发生冲突的地方。在这种情况下,数据源(数组)和数据输出是结果的计数和最终打印。因此,您需要找到一种如何在不同步或锁定这些数据源的情况下执行此操作的方法。幸运的是,Java提供了Atomics类,它允许并发访问而不会阻塞,这些类允许进行大量的智能编码。
对于Array访问,有两个选项:您可以使用AtomicInteger作为索引,并在此处使用线程getAndIncremnt()
来获取其工作索引。如果你知道你有多少线程或数据值有多长,这很有用(提示:不是这样)。
第二个选项是从开头为每个线程分配一个确定性索引,以确保没有线程干扰其他线程。如果你是i.E.有4个线程和256个数据值,你可以用4的增量分配索引值。所以线程A得到0,4,8,......线程B得到1,5,9,......等等。这可确保任何线程都不会与任何其他线程冲突。
最后的计数同样容易:首先让线程在内部计算单词,然后使用addAndGet()
将总和添加到全局AtomicInteger。
现在您只需要确定何时打印该值。答案是“当最后一个线程完成时”。这可以再次完成 - 使用AtomicInteger作为计数器并使每个线程decrementAndGet()
然后检查它们是否为0,这意味着它们是最后一个线程并需要打印结果。然后你可以使用一个Barrier类,它会阻塞,但是在它不再重要的时候,因为所有的工作都是在这一点完成的。