2

考虑两个不能同时执行的方法 a() 和 b()。可以使用 synchronized 关键字来实现这一点,如下所示。我可以按照下面的代码使用 AtomicBoolean 达到相同的效果吗?

final class SynchonizedAB {

synchronized void a(){
   // code to execute
}

synchronized void b(){
  // code to execute
}

}

尝试使用 AtomicBoolean 实现与上述相同的效果:

final class AtomicAB {

private AtomicBoolean atomicBoolean = new AtomicBoolean();

void a(){
   while(!atomicBoolean.compareAndSet(false,true){

  }
  // code to execute
  atomicBoolean.set(false);
}

void b(){
    while(!atomicBoolean.compareAndSet(false,true){

   }
     // code to execute
     atomicBoolean.set(false);
    }

 }
4

3 回答 3

3

不,因为synchronized会阻塞,而AtomicBoolean你会忙着等待。

两者都将确保一次只有一个线程可以执行该块,但是您想让您的 CPU 在 while 块上旋转吗?

于 2014-07-30T14:06:40.410 回答
1

从行为的角度来看,这似乎是 Java 内置同步(监视器锁)的部分替代品。特别是,它似乎提供了正确的互斥,这是大多数人在使用锁时所追求的。

它似乎还提供了正确的内存可见性语义。类Atomic*族具有与 类似的内存语义volatile,因此释放其中一个“锁”将提供与另一个线程获取“锁”的发生前关系,这将提供您想要的可见性保证。

这与 Javasynchronized块的不同之处在于它不提供在异常情况下的自动解锁。要使用这些锁获得类似的语义,您必须将锁定和用法包装在 try-finally 语句中:

void a() {
    while (!atomicBoolean.compareAndSet(false, true) { }
    try {
        // code to execute
    } finally {
        atomicBoolean.set(false);
    }
}

(和类似的b

这个结构似乎确实提供了与Java 的内置监视器锁类似的行为,但总的来说,我觉得这种努力是错误的。从您对另一个答案的评论来看,您似乎有兴趣避免阻塞线程的操作系统开销。发生这种情况时肯定会有开销。但是,Java 的内置锁已经过大量优化,在短期竞争的情况下提供了非常便宜的非竞争锁、偏向锁和自适应自旋循环。在许多情况下,这些尝试中的最后一个是避免操作系统级别的阻塞。通过实现自己的锁,您放弃了这些优化。

当然,您应该进行基准测试。如果您的性能受到操作系统级阻塞开销的影响,则可能是您的锁太粗糙了。减少锁定数量或拆分锁可能是减少争用开销的更有效的方法,而不是尝试实现自己的锁。

于 2014-07-30T16:55:39.273 回答
1

这取决于您计划使用原始同步版本的代码实现的目标。如果在原始代码中添加同步只是为了确保在 a 或 b 方法中一次只存在一个线程,那么对我来说,两个版本的代码看起来都相似。

但是,Kayaman 提到的差异很少。还要添加更多差异,使用同步块,您将获得内存屏障,您将在原子 CAS 循环中错过。但是,如果方法的主体不需要这样的障碍,那么这种差异也会被消除。

原子 cas 循环是否在同步块上表现更好,在个别情况下,只有性能测试可以判断,但这是在并发包中的多个位置遵循的相同技术,以避免块级别的同步。

于 2014-07-30T14:41:52.387 回答