AtomicReference 和 Synchronized 之间有什么区别?
Which is the difference between AtomicReference and Synchronized?
AtomicReference 和 Synchronized 有区别吗?
例如
public class Internet {
AtomicReference<String> address;
public String getAddress(){
return address.toString();
}
public void setAddress(String address) {
this.address.set(address);
}
}
然后我将 class 传递给一些同时尝试使用 class 的线程,如果我使用这个是一样的吗:
public class Internet {
String address;
public String getAddress(){
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
然后在线程使用synchronized
之前访问class?
你在第一个例子中没有初始化引用,应该是:
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 以及处理负载,通常原子方法应该更快。同步方法是一种更通用的机制;使用同步块,您可以更轻松地将多个分配组合在一起,而使用原子引用则涉及更多。
,通过同步,您的线程正在等待锁;没有超时,死锁是可能的。使用原子引用,线程无需等待共享锁即可进行更改。
最简单、性能最好的实现方式是组织代码,使对象不可变,从而避免所有锁定、忙等待和缓存更新:
public final class Internet {
private final String s;
public Internet(String s) {
this.s = s;
}
public String getAddress() {return s;}
}
按偏好降序排列:
- 尽可能使用不变性。
- 对于不能不可变的代码,尝试将变更限制在一个线程中。
- 如果只有一件事必须跨线程更改,请使用原子方法。
- 如果跨线程的多个更改需要一起发生而不受其他线程的干扰,请使用锁定。
A synchronized
method/block 在一个线程执行该方法时阻止其他线程对该 method/block 的所有访问。
一个 Atomic...
可以同时被多个线程访问 - 通常有 CAS 访问方法可供他们使用,以帮助实现高速访问。
因此 - 它们完全不同,但有时可用于解决并行可访问性问题。
这两个 类 使用两种不同的方法来 return 一个稳定增加的数字,这样同一个数字永远不会被发送两次。 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++;
}
}
这里的其他答案没有错如果你能理解它们,但它们似乎大多关注细节、术语和用例,而跳过了大问题"everybody" 已经知道的图片。
这是大图——AtomicFoobar
操作和 synchronized
块之间的区别。
AtomicFoobar 操作(例如,atomicReference.compareAndSet(...))要么只执行一个非常简单的线程安全操作,要么失败。不管成功还是失败,都不会让线程等待。
A synchronized
块,另一方面,它和你做的一样复杂---锁被锁定时执行的语句数量没有限制。 synchronized
块将 永远不会 失败,但它 可能 使调用线程等待,直到可以安全地执行操作。
在大多数体系结构中,每个 AtomicFoobar 方法都被实现为执行单个专用硬件指令的 Java 本地方法 (即 C 代码)。另一方面,同步通常是通过操作系统调用实现的,操作系统调用在内部深处可能使用相同的硬件指令。
AtomicReference 和 Synchronized 有区别吗?
例如
public class Internet {
AtomicReference<String> address;
public String getAddress(){
return address.toString();
}
public void setAddress(String address) {
this.address.set(address);
}
}
然后我将 class 传递给一些同时尝试使用 class 的线程,如果我使用这个是一样的吗:
public class Internet {
String address;
public String getAddress(){
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
然后在线程使用synchronized
之前访问class?
你在第一个例子中没有初始化引用,应该是:
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 以及处理负载,通常原子方法应该更快。同步方法是一种更通用的机制;使用同步块,您可以更轻松地将多个分配组合在一起,而使用原子引用则涉及更多。
最简单、性能最好的实现方式是组织代码,使对象不可变,从而避免所有锁定、忙等待和缓存更新:
public final class Internet {
private final String s;
public Internet(String s) {
this.s = s;
}
public String getAddress() {return s;}
}
按偏好降序排列:
- 尽可能使用不变性。
- 对于不能不可变的代码,尝试将变更限制在一个线程中。
- 如果只有一件事必须跨线程更改,请使用原子方法。
- 如果跨线程的多个更改需要一起发生而不受其他线程的干扰,请使用锁定。
A synchronized
method/block 在一个线程执行该方法时阻止其他线程对该 method/block 的所有访问。
一个 Atomic...
可以同时被多个线程访问 - 通常有 CAS 访问方法可供他们使用,以帮助实现高速访问。
因此 - 它们完全不同,但有时可用于解决并行可访问性问题。
这两个 类 使用两种不同的方法来 return 一个稳定增加的数字,这样同一个数字永远不会被发送两次。 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++;
}
}
这里的其他答案没有错如果你能理解它们,但它们似乎大多关注细节、术语和用例,而跳过了大问题"everybody" 已经知道的图片。
这是大图——AtomicFoobar
操作和 synchronized
块之间的区别。
AtomicFoobar 操作(例如,atomicReference.compareAndSet(...))要么只执行一个非常简单的线程安全操作,要么失败。不管成功还是失败,都不会让线程等待。
A synchronized
块,另一方面,它和你做的一样复杂---锁被锁定时执行的语句数量没有限制。 synchronized
块将 永远不会 失败,但它 可能 使调用线程等待,直到可以安全地执行操作。
在大多数体系结构中,每个 AtomicFoobar 方法都被实现为执行单个专用硬件指令的 Java 本地方法 (即 C 代码)。另一方面,同步通常是通过操作系统调用实现的,操作系统调用在内部深处可能使用相同的硬件指令。