Java:使代码块成为原子

时间:2014-10-29 15:10:22

标签: java concurrency race-condition

考虑一下我有这段Java代码

我想知道是否有一种无锁机制可以使突出显示的代码片段成为原子。我想避免当有人拨打fetchSomeThing()时,我正处于BlockX的中间,fetchSomeThing()正在查看A的新副本,但是B和C的旧副本。

public class MyClass
{
   private volatile Map a, b, c; 

   public void refresh()
   {
      Map a_ = loadA();   
      Map b_ = loadB();
      Map c_ = loadC();

      //I want to make the following block atomic
      // call it Block X
      {
         this.a = a_;
         this.b = b_;
         this.c = c_;
      }
   }

   public String fetchSomeThing()
   {
       // some joint operations on this.a, this.b and this.c
   }
}

我能想到的唯一方法是将它分成两个类,并将a,b,c包装在一个对象中。

但这非常麻烦。有没有更好的办法?

public class MyShadowClass
{
   private Map a, b, c; 
   public MyShadowClass()
   {
      init();
   }

   public void init()
   {
      Map a_ = loadA();   
      Map b_ = loadB();
      Map c_ = loadC();

      this.a = a_;
      this.b = b_;
      this.c = c_;
   }

   public String fetchSomeThing()
   {
       // some joint operations on this.a, this.b and this.c
   }
}

public class MyClass
{
   private volatile MyShadowClass shadow; 
   public void refresh()
   {
      MyShadowClass tempShadow = new MyShadowClass();
      shadow = tempShadow; 
   }

   public String fetchSomeThing()
   {
      return shadow.fetchSomeThing();
   }

}

1 个答案:

答案 0 :(得分:1)

只需使复合对象保持当前有效状态即可。然后,您可以使用AtomicReference来管理当前状态:

 private AtomicReference<Thing> currentThing = new AtomicReference<Thing>();

 public Thing getThing() {
      while (true) {
          Thing result = currentThing.get();
          if (result != null)
              return result;
          result = buildThing();
          if (currentThing.compareAndSet(null, result);
              return result;
      }
 }

 private Thing buildThing() {
      // whatever, only important thing is that this creates
      // a new and valid instance of Thing
 }

 public void refresh() {
      Thing freshThing = buildThing();
      currentThing.set(freshThing);
 }

如果你可以确保只有一个线程写入该字段,或者你并不特别关心在refresh()和initial时获取相同的实例,那么你可以简单地将“currentThing”声明为正常,但是挥发性字段get正在同时执行。