AtomicReference 和 Synchronized 之间有什么区别?

Which is the difference between AtomicReference and Synchronized?

A​​tomicReference 和 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 代码)。另一方面,同步通常是通过操作系统调用实现的,操作系统调用在内部深处可能使用相同的硬件指令。