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

Tin*_*wor 10 java multithreading atomic synchronized

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

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

}
Run Code Online (Sandbox Code Playgroud)

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

public class Internet {
    String address;
    public String getAddress(){
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Nat*_*hes 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

访问限制所在的位置很重要.如果你把控件放在被访问的对象中,那么它可以单独控制它的不变量,这比依赖线程正确地进行所有同步要脆弱得多,其中一个行为不当的访问线程可能破坏被访问的东西.所以第一个例子在该帐户上要好得多.

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

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后它是一个更接近的比较,一个依赖于锁定而另一个依赖于原子引用.使用AtomicReference的那个尝试使用机器级原子处理指令来避免锁定.哪个更快可能取决于您的硬件和jvm以及处理负载,通常原子方法应该更快.同步方法是一种更通用的机制; 使用synchronized块,您可以更轻松地将多个分配组合在一起,其中使用原子引用更加复杂.

正如詹姆斯在他的回答中所说,在同步时你的线程正在等待锁定; 没有超时,死锁是可能的.使用原子引用,线程进行更改而不等待共享锁.

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

public final class Internet {
    private final String s;
    public Internet(String s) {
        this.s = s;
    }
    public String getAddress() {return s;}
}
Run Code Online (Sandbox Code Playgroud)

按优先顺序降序排列:

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


Sol*_*low 6

如果您能理解这些答案,那么这里的其他答案并没有什么问题,但它们似乎主要集中在细节、术语和用例上,而跳过了“每个人”已经知道的大局。

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

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

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

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