AtomicReference和Synchronized之间的区别是什么?

时间:2015-03-03 14:36:11

标签: java multithreading atomic synchronized

AtomicReference和Synchronized之间有什么区别吗? E.G。

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才能访问类?

3 个答案:

答案 0 :(得分:9)

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

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以及处理负载,通常原子方法应该更快。同步方法是一种更通用的机制;使用synchronized块,您可以更轻松地将多个分配组合在一起,其中使用原子引用它涉及更多。

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;}
}

按优先顺序降序:

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

答案 1 :(得分:4)

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

以下是全局图--- AtomicFoobar操作与synchronized块之间的区别。

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

另一方面,synchronized块与您所做的一样复杂 - 锁定锁定时执行的语句数量没有限制。 synchronized永远不会失败,但可能使调用线程等待,直到可以安全地执行操作。

在大多数体系结构中,每个AtomicFoobar方法都实现为Java 本机方法(即C代码),它执行单个专用硬件指令。另一方面,同步最常用操作系统调用来实现,这些操作系统调用可能会使用相同的硬件指令。

答案 2 :(得分:2)

当一个线程正在执行该方法时,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++;
    }

}