【问题标题】:Which is the difference between AtomicReference and Synchronized?AtomicReference 和 Synchronized 有什么区别?
【发布时间】:2015-03-03 14:36:11
【问题描述】:

AtomicReference 和 Synchronized 之间有什么区别吗?
例如

public class Internet {
    AtomicReference<String> address;
    public String getAddress(){
        return address.toString();
    }
    public void setAddress(String address) {
        this.address.set(address);
    }

}

并且我将类传递给一些尝试同时使用该类的线程,如果我使用这个是不是同样的事情:

public class Internet {
    String address;
    public String getAddress(){
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}

然后在线程中使用synchronized之前访问类?

【问题讨论】:

  • 功能是一样的,如果你要问的话。并发模型不同。
  • 参见docs.oracle.com/javase/tutorial/essential/concurrency/… “对于这个简单的类,同步是可以接受的解决方案。但对于更复杂的类,我们可能希望避免不必要的同步对活动性的影响。”如上所述,它们在功能上是相同的。

标签: java multithreading atomic synchronized


【解决方案1】:

第一个例子中你没有初始化引用,应该是:

public class Internet {
    AtomicReference<String> address = new AtomicReference<String>();
    public String getAddress(){
        String s = address.get();
        return s == null ? null : s.toString();
    }
    public void setAddress(String address) {
        this.address.set(address);
    }
}

访问限制的位置很重要。如果您将控件放在被访问的对象中,那么它可以单独控制其不变量,这比依赖线程来正确同步要脆弱得多,其中一个行为不良的访问线程可能会破坏正在访问的东西。因此,第一个示例在该帐户上要好得多。

如果您更改第二个示例,以便对象可以控制自己的锁定(因此它不依赖线程访问它来安全地执行此操作),如下所示:

public class Internet {
    private final Object lock = new Object();
    private String s;
    public String getAddress() {
       synchronized(lock) {
           return s;
       }
    }
    public void setAddress(String s) {
        synchronized(lock) {
            this.s = s;
        }
    }
}

然后进行更仔细的比较,一个依赖于锁定,另一个依赖于原子引用。使用 AtomicReference 的方法试图避免使用机器级原子处理指令进行锁定。哪个更快可能取决于您的硬件和 jvm 以及处理负载,通常原子方法应该更快。同步方法是一种更通用的机制;使用同步块,您可以更轻松地将多个分配组合在一起,而原子引用则涉及更多。

As James says in his answer,通过同步,您的线程正在等待锁定;没有超时,并且可能出现死锁。使用原子引用,线程无需等待共享锁即可进行更改。

实现这一点的最简单且性能最佳的方法是组织您的代码,以便您可以使对象不可变,从而避免所有锁定、忙等待和缓存更新:

public final class Internet {
    private final String s;
    public Internet(String s) {
        this.s = s;
    }
    public String getAddress() {return s;}
}

按偏好降序排列:

  • 尽可能选择不变性。
  • 对于不能不可变的代码,尝试将突变限制在线程中。
  • 如果只需要跨线程更改一件事,请使用原子方法。
  • 如果需要在不受其他线程干扰的情况下跨线程进行多项更改,请使用锁定。

【讨论】:

    【解决方案2】:

    这里的其他答案没有任何问题如果您可以理解它们,但它们似乎主要关注细节、术语和用例,而忽略了“每个人”的大局已经知道了。

    这是大局——AtomicFoobar 操作和 synchronized 块之间的区别。

    AtomicFoobar 操作(例如 atomicReference.compareAndSet(...))要么执行一个非常简单的线程安全操作,要么失败。不管成功还是失败,都不会让线程等待。

    另一方面,synchronized 块和你做的一样复杂——锁被锁定时执行的语句数没有限制。 synchronized 块将永远失败,但它可能使调用线程等待,直到可以安全地执行操作。

    在大多数架构上,每个 AtomicFoobar 方法都实现为执行单个专用硬件指令的 Java 本机方法(即 C 代码)。另一方面,同步通常是通过操作系统调用来实现的,这些调用在内心深处可能使用相同的硬件指令。

    【讨论】:

      【解决方案3】:

      当一个线程正在执行该方法时,synchronized 方法/块会阻止其他线程对该方法/块的所有访问。

      一个Atomic... 可以同时被多个线程访问——通常有CAS 访问方法可供它们使用以帮助进行高速访问。

      因此 - 它们完全不同,但有时可用于解决并行可访问性问题。

      这两个类使用两种不同的方法返回一个稳定增长的数字,这样同一个数字就不会被传递两次。 AtomicInteger 版本将在高负载环境中运行得更快。使用synchronized 的那个可以在Java 4 和更早版本中工作。

      public class Incremental1 {
      
          private AtomicInteger n = new AtomicInteger();
      
          public Integer nextNumber() {
              // Use the Atomic CAS function to increment the number.
              return n.getAndIncrement();
          }
      }
      
      public class Incremental2 {
      
          private int n = 0;
      
          public synchronized Integer nextNumber() {
              // No two threads can get in here at the same time.
              return n++;
          }
      
      }
      

      【讨论】:

      • 我想我在 JCIP 中读到,通常原子优先用于中低竞争,而不是高负载?
      • @NathanHughes - 取决于您所说的高负载是什么意思。原子在除异常高负载之外的所有情况下都能很好地工作。使用原子,您通常必须在原子上自旋锁定,并且在非常高的负载下,这可能会导致巨大的退化,但在这种情况下,synchronized 很久以前就会陷入困境。
      • @OldCurmudgeon 你的最后一句话并不完全正确。在较高的争用下,BlockingQueue 的性能优于ConcurrentLinkedQueue。不会失败,而只是线程调度。所以我会说内森的说法是正确的。
      • @JohnVint - BlockingQueueinterface - 你指的是BlockingQueue 的哪种形式?快速查看 ConcurrentLinkedQueueLinkedBlockingQueue 的来源建议两者都使用原子 - 我看不出你的意思,你能扩展一下吗?
      • @OldCurmudgeon 任何BlockingQueue 都将使用锁或synchronized 否则不会阻塞。 ConcurrentLinkedQueue 特别是非阻塞的。所以我的意思是 ArrayBlockingQueue 或 LinkedBlockingQueue 在高竞争中的表现都比 ConcurrentLinkedQueue 好。
      猜你喜欢
      • 2012-02-21
      • 2015-01-30
      • 1970-01-01
      • 2016-07-25
      • 2013-04-14
      • 2015-03-25
      • 2012-02-05
      • 2011-03-31
      相关资源
      最近更新 更多