java 并发编程中,读取值时是否需要使用synchronized?
in java concurrent programming, is need to using synchronized when read value?
这个class可以在多线程中使用,因为它是线程安全的。
public class Hello {
private int value = 0;
public synchronized int get() {
return value;
}
public synchronized void set(int value) {
this.value = value;
}
}
我知道我们必须在 get() 时使用同步的原因,除了 set()是内存可见性。
和java volatile 关键字可用于内存可见性。
那么..这个class也是线程安全的??
public class Hello {
private volatile int value = 0;
public int get() {
return value;
}
public synchronized void set(int value) {
this.value = value;
}
}
在您的示例中,没有必要在 get()
和 set
方法中使用 synchronized
,因为 value
属性是用 volatile
关键字。
这是因为 volatile
关键字在编写器和 reader 线程之间强制执行 happens-before 关系。
Java Language Specification, Section 8.3.1.4. volatile fields:
The Java programming language allows threads to access shared variables (§17.1). As a rule, to ensure that shared variables are consistently and reliably updated, a thread should ensure that it has exclusive use of such variables by obtaining a lock that, conventionally, enforces mutual exclusion for those shared variables.
The Java programming language provides a second mechanism, volatile fields, that is more convenient than locking for some purposes.
A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable (§17.4).
因此,在您的情况下,无需同步 get()
和 set()
方法。
在您的具体示例中,您不需要额外的同步。鉴于您已经提到了内存可见性(又名 happens-before),我不会更深入地解释这一点。
但是,它并不普遍适用。您的示例中有几个假设足以简单地使用 volatile
类型 value
尽管您只是在简单地执行 value
的 retrieval/assignment,但并不总是保证对所有数据类型都是原子的。 IIrc,Java 只保证这种操作对于 int
和小于 int
的类型是原子的。这意味着,例如,如果 value
是 long
类型,即使你用 volatile
声明了它,你仍然可以用上面的例子 value
破坏
在 value
上的操作
好的,我们假设它是 int
。在您的示例中,您只是获取并分配一个 int
操作是原子的,因此只需使用 volatile
就足够了。但是,如果您有另一种方法来执行类似 value++
的操作,则使用 volatile
是不够的。在这种情况下,您可以考虑使用 Atomic*
或使用 synchronized
更新:后来我发现 JLS https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.7 提到使用 volatile 会在 double/long 上强制执行 atomic read/write。所以我原来的第1点其实是错误的
在您的 class Hello
中只有一个字段 int value
。同步锁定整个对象,因此操作繁重,您可以使用 AtomicInteger。由于 Atomic* 变量更快。 Volatile 只满足 "happens before" 你不能实现像 "check then act" 这样的操作的原子性与 volatiles.
要使Hello
class线程安全,最好的方法
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);
}
}
如this post中所述
If some object has only one field or its critical updates are
limited to only one field of object so instead of using
synchronization or other thread safe collections , Atomic variables
(AtlomicInteger, AtomicReference etc.) can be utilized.
这个class可以在多线程中使用,因为它是线程安全的。
public class Hello {
private int value = 0;
public synchronized int get() {
return value;
}
public synchronized void set(int value) {
this.value = value;
}
}
我知道我们必须在 get() 时使用同步的原因,除了 set()是内存可见性。
和java volatile 关键字可用于内存可见性。
那么..这个class也是线程安全的??
public class Hello {
private volatile int value = 0;
public int get() {
return value;
}
public synchronized void set(int value) {
this.value = value;
}
}
在您的示例中,没有必要在 get()
和 set
方法中使用 synchronized
,因为 value
属性是用 volatile
关键字。
这是因为 volatile
关键字在编写器和 reader 线程之间强制执行 happens-before 关系。
Java Language Specification, Section 8.3.1.4. volatile fields:
The Java programming language allows threads to access shared variables (§17.1). As a rule, to ensure that shared variables are consistently and reliably updated, a thread should ensure that it has exclusive use of such variables by obtaining a lock that, conventionally, enforces mutual exclusion for those shared variables.
The Java programming language provides a second mechanism, volatile fields, that is more convenient than locking for some purposes.
A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable (§17.4).
因此,在您的情况下,无需同步 get()
和 set()
方法。
在您的具体示例中,您不需要额外的同步。鉴于您已经提到了内存可见性(又名 happens-before),我不会更深入地解释这一点。
但是,它并不普遍适用。您的示例中有几个假设足以简单地使用 volatile
类型
value
尽管您只是在简单地执行value
的 retrieval/assignment,但并不总是保证对所有数据类型都是原子的。 IIrc,Java 只保证这种操作对于int
和小于int
的类型是原子的。这意味着,例如,如果value
是long
类型,即使你用volatile
声明了它,你仍然可以用上面的例子value
破坏在
上的操作value
好的,我们假设它是
int
。在您的示例中,您只是获取并分配一个int
操作是原子的,因此只需使用volatile
就足够了。但是,如果您有另一种方法来执行类似value++
的操作,则使用volatile
是不够的。在这种情况下,您可以考虑使用Atomic*
或使用synchronized
更新:后来我发现 JLS https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.7 提到使用 volatile 会在 double/long 上强制执行 atomic read/write。所以我原来的第1点其实是错误的
在您的 class Hello
中只有一个字段 int value
。同步锁定整个对象,因此操作繁重,您可以使用 AtomicInteger。由于 Atomic* 变量更快。 Volatile 只满足 "happens before" 你不能实现像 "check then act" 这样的操作的原子性与 volatiles.
要使Hello
class线程安全,最好的方法
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);
}
}
如this post中所述
If some object has only one field or its critical updates are limited to only one field of object so instead of using synchronization or other thread safe collections , Atomic variables (AtlomicInteger, AtomicReference etc.) can be utilized.