在java并发编程中,需要在读取值时使用synchronized



这个类可以在多线程中使用,因为它是线程安全的。

public class Hello {
    private int value = 0;
    public synchronized int get() {
        return value;
    }
    public synchronized void set(int value) {
        this.value = value;
    }
}

我知道,除了set()之外,我们在get()时必须使用synchronized的原因是内存可见性。

java volatile关键字可以用于内存可见性。

那么。。这个类也是线程安全的??

public class Hello {
    private volatile int value = 0;
    public int get() {
        return value;
    }
    public synchronized void set(int value) {
        this.value = value;
    }
}

在您的特定示例中,您不需要额外的synchronized。鉴于您已经提到了内存可见性(也就是以前发生过的事情),我不会对此进行更深入的解释。

然而,它并不普遍适用。在您的示例中,有几个假设足以简单地使用volatile

  1. value 类型

  2. 尽管您只是简单地检索/分配value,但它并不总是保证对所有数据类型都是原子的。Iirc,Java只保证这样的操作对于int和小于int的类型是原子操作。这意味着,例如,如果value的类型是long,即使您已经用volatile声明了它,您仍然可能使用上面的示例损坏value
  3. value 上的操作

    好的,假设它是int。在您的示例中,您只需获取并分配一个操作为原子的int,因此只需使用volatile就足够了。但是,如果您有另一种方法来执行类似value++的操作,那么仅使用volatile是不够的。在这种情况下,您可以考虑使用Atomic*或使用synchronized


更新:我后来发现JLShttps://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.7提到使用volatile将在double/long上强制执行原子读/写。所以我最初的观点1实际上是错误的

在您的示例中,get()set方法都不需要使用synchronized,因为value属性是用volatile关键字声明的。

这是因为volatile关键字强制在编写器和读取器线程之间的关系之前发生

Java语言规范,第8.3.1.4节。易失性字段:

Java编程语言允许线程访问共享变量(§17.1)。通常,为了确保共享变量得到一致和可靠的更新,线程应通过获得锁来确保其对这些变量的独占使用,该锁通常会强制执行这些共享变量的互斥。

Java编程语言提供了第二种机制,即volatile字段,它在某些方面比锁定更方便。

字段可以被声明为volatile,在这种情况下,Java内存模型确保所有线程都能看到变量的一致值(§17.4)

因此,在您的情况下,不需要同步get()set()方法。

在类Hello中,只有一个字段int value。Synchronized会锁定整个对象,从而导致繁重的操作,相反,您可以使用AtomicInteger。由于Atomic*变量更快。Volatile只是满足"发生之前"的条件,对于像"检查然后操作"这样的操作,您无法实现对Volatile的原子性。

为了使Hello类线程安全,的最佳方式

import java.util.concurrent.atomic.AtomicInteger;
public class Hello {
    private AtomicInteger value;
    public int get() {
        return this.value.get();
    }
    public synchronized void set(int value) {
        this.value.set(value);
    }
}

如本文所述

如果某个对象只有一个字段或其关键更新为仅限于对象的一个字段,因此不使用同步或其他线程安全集合,原子变量(AtomicInteger、AtomicReference等)。

相关内容

  • 没有找到相关文章

最新更新