这个类可以在多线程中使用,因为它是线程安全的。
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
-
value
类型 value
上的操作好的,假设它是
int
。在您的示例中,您只需获取并分配一个操作为原子的int
,因此只需使用volatile
就足够了。但是,如果您有另一种方法来执行类似value++
的操作,那么仅使用volatile
是不够的。在这种情况下,您可以考虑使用Atomic*
或使用synchronized
value
,但它并不总是保证对所有数据类型都是原子的。Iirc,Java只保证这样的操作对于int
和小于int
的类型是原子操作。这意味着,例如,如果value
的类型是long
,即使您已经用volatile
声明了它,您仍然可能使用上面的示例损坏value
更新:我后来发现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等)。