如果我有一个Java 8个程序是由多个线程读取和写入的一个简单的整数。
如果我告诉应用程序需要支持高吞吐量读取和很少写 - 这个问题的答案很简单,我只是用一个读写锁。然后,多个线程可以执行与不堵的同时读取 - 只有当阻塞不常写操作完成时发生。
但在情况下,如果有人告诉我的应用程序需要支持高吞吐量写入(即共享变量是由不同的线程频繁更新)。无论我可以看到什么样的锁我在这里使用,尽可能的将总是导致线程阻塞 - 当一个线程获得的变量和更新锁,它的其余线程也试图更新该变量将不得不等待,直到他们得到的锁定 - 这是正确的还是我失去了在Java中8的东西吗?
我可以去,并且写一些排序上的共享变量,其中线程调用它立即返回更新方法的异步更新方法,我使用某种形式的被窝里的数据结构来排队写入共享变量。至少这样我会阻止线程试图更新共享变量时阻塞。诚然这种方法会提高其他问题,如应线程承担其保证写DEF。成功或者我应该提供一个回调,以通知更新是否成功等。除了这样的事情,我看不出有什么办法一轮阻塞在Java中使用8锁定任何高通量写什么时候? (或者我应该接受阻塞,只是,即使在高吞吐量的情况下写使用锁反正)。谢谢
严格地说Integer
的 - 你可以使用LongAdder
,它的实现是完全针对你的情况看来。如果你关心这里有一些额外的细节。
它采用CAS
(比较和交换)的引擎盖下,很像AtomicLong
,但有一些不同之处。首先,它拥有被包裹在一个所谓的long value
实际Cell
的 - 基本上是一类允许cas
的value
(比较和交换),以新的价值,就像一个二传手,如果你想要的。这Cell
还标注有@sun.misc.Contended
以防止共享的错误;这里是它的解释(从代码中的注释):
但居住在阵列原子对象将倾向于相邻放置对方,等会多数情况下没有这种预防措施共享的高速缓存行(与一个巨大的性能产生负面影响)。
实施是从这里很有趣。让我们来看看,当你调用add(long x)
方法会发生什么:
public void add(long x) {
Cell[] cs; long b, v; int m; Cell c;
if ((cs = cells) != null || !casBase(b = base, b + x)) {
boolean uncontended = true;
if (cs == null || (m = cs.length - 1) < 0 ||
(c = cs[getProbe() & m]) == null ||
!(uncontended = c.cas(v = c.value, v + x)))
longAccumulate(x, null, uncontended);
}
}
我们的想法是,如果Cell [] cs
为null,之前有没有竞争,这意味着long value
或者是未初始化或以前所有CAS
操作已经被所有线程成功。在这种情况下,尝试新的价值CAS
到long value
- 是否奏效,我们就完成了。如果,虽然失败了,也创造了Cell []
的阵列,使每个单独的线程试图在自己的空间里工作,最大限度地减少争。
下一句是什么,你真正关心的,如果我理解正确你的问题(这是我的,不会从代码中的注释来在所有):
在简单的话:如果线程之间没有竞争,工作仿佛
AtomicLong
使用(在某种程度上)完成,否则试图创建一个单独的空间,每个线程去努力。
如果你关心一些额外的细节,我发现有趣:
所述Cell[]
总是两个(很像HashMap
内部阵列)的功率;然后每个线程使用ThreadLocalRandom
创造一些的hashCode,试图找到在阵列Cell [] cs
条目写,甚至再重新使用散列以Marsaglia XorShif
试图找到这个阵列中的空闲插槽;数组的大小上限,以你(最近的两个从实际功率)内核的数量,这个数组可以调整大小,因此它可以成长,所有这些操作都使用volatile int cellsBusy
自旋锁完成。此代码是一流的,但正如所说,我没有得到这一切。