【问题标题】:Java - Compare and Swap and synchronized BlockJava - 比较和交换和同步块
【发布时间】:2020-11-10 17:43:18
【问题描述】:
public class SimulatedCAS { 
  private int value;

  public synchronized int get() { return value; }

  public synchronized int compareAndSwap(int expectedValue, int newValue) 
  {
  int oldValue = value; 
  if (oldValue == expectedValue) 
      value = newValue;
  return oldValue; 
  }
}

public class CasCounter 
{ 
   private SimulatedCAS value;

   public int getValue() 
   { 
     return value.get();
   }
  
  public int increment() 
  { 
      int value.get(); 
      while (v != value.compareAndSwap(v, v + 1)) 
      {
         v = value.get(); 
      }
  }

}

我参考了一本书《Java 并发实践》

一个计数器必须由多个线程增加。我尝试使用比较和交换方法,但最后它使用了同步关键字,这可能再次导致线程阻塞和等待。使用同步块为我提供了与任何人相同的性能。使用比较和交换和同步块有什么区别?或任何其他在不使用同步块的情况下实现比较和交换的方式。

【问题讨论】:

  • 您可以使用Unsafe.getUnsafe().compareAndSwapInt()来实现比较和交换。一般来说,CAS 比同步更有效。
  • 为什么不直接使用AtomicInteger呢?不需要自己做
  • 不,我不能使用原子整数。这是我的任务@Lino-Votedon'tsayThanks
  • Unsafe.getUnsafe.compareAndSwapInt() 用于 AtomicInteger 类。它需要一个对象,并且需要很长时间……对吗? @weaver 与同步块有何不同

标签: java concurrency synchronization


【解决方案1】:

我需要用多个线程递增计数器

AtomicInteger class 非常适合。

你可以用final AtomicInteger i=new AtomicInteger(initial_value);创建它然后你可以调用i.set(new_value)来设置它的值,你可以调用i.get()来获取它的值,最重要的是你的应用程序,你可以调用i.incrementAndGet()来原子地递增值。

如果 N 个不同的线程都“同时”调用i.incrementAndGet(),那么

  • 保证每个线程看到不同的返回值,并且
  • 保证全部完成后的最终值恰好增加 N。

AtomicInteger 类还有很多其他方法。他们中的大多数都对当多个线程访问变量时会发生什么做出有用的保证。

【讨论】:

  • 有没有其他方法可以做到这一点,而不使用 Atomic Integer ? @所罗门慢
  • @RubanThilak,我不明白。你想做的“这个”是什么?如果您只想让 Java 程序中的不同线程增加相同的共享计数器,那么AtomicInteger 几乎肯定是最好的方法。下一个最好的是synchronized(counter_mutex){counter=counter+1;}。但是,如果您想满足特定要求(例如,家庭作业),请说明:您的实际要求是什么?
  • 很抱歉打扰你了,我需要找到在不阻塞其他线程的情况下提供最佳性能的方法。
【解决方案2】:

Real Compare and Swap 执行乐观锁定。它会更改值,然后如果同时更改了变量,则进行回滚。因此,如果变量很少被修改,那么 CAS 的性能要比同步的好。

但是如果变量经常被修改,那么同步的表现会更好,因为它不允许任何东西在变量被改变时弄乱。因此无需进行昂贵的回滚。

【讨论】:

  • 可以在不使用 syncronized 的情况下进行比较和交换吗? @llya Sazonov
  • @RubanThilak,没有不锁定锁的比较和交换 (CAS) 的纯 Java 实现。 AtomicXxxxxxx 类具有native implementations,它们使用特定于平台的 硬件指令来实现无锁CAS。您无法使用纯 Java 代码访问任何特定于平台的硬件功能。这是 Java 早期“一次编写,随处运行”理念的结果。
  • @SolomonSlow 那么刚刚同步块与比较和交换之间的区别是什么。比较和交换方法使用同步,这可能会导致将该方法锁定到一个特定线程并迫使其他线程等待。
  • @RubanThilak,你问他们做什么?他们做完全不同的事情。 synchronized 块是一个代码块,如果不“锁定”互斥锁,任何线程都无法进入,而互斥锁是一次只能被一个线程“锁定”的东西。另一方面,CAS 是一个处理三个操作数的运算符;一个变量、一个期望值和一个新值。它以原子方式将变量的内容与预期值进行比较,然后当且仅当当前值和预期值匹配时将其设置为新值。
  • @RubanThilak,你问的是实施吗?您的模拟 CAS 使用synchronized,但现代工作站/服务器/移动设备 CPU 可以使用单个硬件指令执行 CAS。事实上,几乎可以肯定,被synchronized 块锁定的互斥锁将使用硬件CAS指令来实现。
猜你喜欢
  • 2011-05-10
  • 1970-01-01
  • 1970-01-01
  • 2012-03-10
  • 2011-05-11
  • 2013-10-27
  • 1970-01-01
  • 2016-01-15
  • 2021-03-09
相关资源
最近更新 更多