我想编写一个简单的线程安全类,可以用来设置或获取一个整数值。
最简单的方法是使用同步关键字:
public class MyIntegerHolder {
private Integer value;
synchronized public Integer getValue() {
return value;
}
synchronized public void setValue(Integer value) {
this.value = value;
}
}我也可以尝试使用易失性。
public class MyIntegerHolder {
private volatile Integer value;
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
}是具有易失性关键字线程安全的类。
考虑以下事件的顺序:
根据Java语言规范,
但我看不出如何从规范中得出"1“发生在"2”之前,因此我怀疑"1“不会在"2”之前发生。
我怀疑线程C可能读取7或5。我认为带有易失性关键字的类不是线程安全的,并且也可以使用以下顺序:
我是否正确地假设MyIntegerHolder与易失性不是线程安全的?
是否可以使用AtomicInteger制作线程安全的整数保持架:
public class MyIntegerHolder {
private AtomicInteger atomicInteger = new AtomicInteger();
public Integer getValue() {
return atomicInteger.get();
}
public void setValue(Integer value) {
atomicInteger.set(value);
}
}下面是实践书中Java并发性的一个片段:
“原子变量的读写具有与易失性变量相同的内存语义。”
编写线程安全MyIntegerHolder的最好方法(最好是非阻塞)是什么?
如果你知道答案,我想知道你为什么认为它是正确的。它符合规格吗?如果是这样的话,是怎么做的?
发布于 2013-05-03 19:15:06
这个问题对我来说并不容易,因为我认为(错误地)了解了发生的一切--在关系之前--使人完全了解了Java模型和易失性的语义。
我在本文中找到了最好的解释:"JSR-133: JavaTM内存模型和线程规范“
上述文件中最相关的部分是"7.3格式良好的处决“一节。
Java内存模型保证程序的所有执行都是格式良好的。执行只有在以下情况下才是正确的。
在一致性之前发生,通常足以得出关于程序行为的结论--但在这种情况下,因为不发生易失性写入--在另一个易失性写入之前就足够了。
MyIntegerHolder与易失性是线程安全的,但它的安全性来自同步顺序的一致性。
在我看来,当线程B即将将值设置为7时,A直到那一刻才通知B它所做的一切(其他答案之一)-它只告诉B易失变量的值。如果线程B所采取的操作是读的而不是写的(在这种情况下,在这两个线程所采取的操作之间的关系之前),线程A将通知B所有事情(将值赋值给其他变量)。
发布于 2013-05-03 12:15:07
关键字synchronized表示,如果Thread A and Thread B想要访问Integer,它们就不能同时访问。A在告诉B等我做完了再说。
另一方面,volatile使线程更加“友好”。他们开始互相交谈,一起工作来完成任务。所以当B试图访问时,A会告诉B它在那一刻之前所做的一切。B现在知道这些变化,可以从A离开的地方继续其工作。
在Java中,由于这个原因,您有Atomic,它在封面下使用volatile关键字,所以他们做的事情几乎是一样的,但是它们节省了您的时间和精力。
你要找的是AtomicInteger,你说得对。对于您要执行的操作,这是最好的选择。
There are two main uses of `AtomicInteger`:
* As an atomic counter (incrementAndGet(), etc) that can be used by many threads concurrently
* As a primitive that supports compare-and-swap instruction (compareAndSet()) to implement non-blocking algorithms. 回答您的问题,在一个普通的注意事项
这取决于你需要什么。我并不是说synchronized是错的,volatile是好的,否则很久以前很好的volatile就会移除synchronized。没有绝对的答案,有很多具体的案例和使用场景。
我的几个书签:
核心Java并发
Java并发
更新
来自Java并发规范可用的这里
封装java.util.concurrent.atomic 支持对单变量进行无锁线程安全编程的类的小型工具包。
Instances of classes `AtomicBoolean`, `AtomicInteger`, `AtomicLong`, and `AtomicReference` each provide access and updates to a single variable of the corresponding type.
Each class also provides appropriate utility methods for that type.
For example, classes `AtomicLong` and AtomicInteger provide atomic increment methods.
The memory effects for accesses and updates of atomics generally follow the rules for volatiles:
get has the memory effects of reading a volatile variable.
set has the memory effects of writing (assigning) a volatile variable.也是来自这里的
Java编程语言volatile关键字:
(在Java的所有版本中)对易失变量的读和写都有全局排序。这意味着每个访问易失性字段的线程将在继续之前读取其当前值,而不是(潜在地)使用缓存的值。(但是,对于易失性读和写与常规读和写的相对顺序没有保证,这意味着它通常不是有用的线程结构。)
发布于 2013-05-03 12:14:30
如果只需要对变量进行get / set,那么就足以像以前那样声明它的易失性。如果您检查AtomicInteger设置/获取工作的方式,您将看到相同的实现
private volatile int value;
...
public final int get() {
return value;
}
public final void set(int newValue) {
value = newValue;
}但你不能这样简单地增加一个易失性的字段。这就是我们使用AtomicInteger.incrementAndGet或getAndIncrement方法的地方。
https://stackoverflow.com/questions/16358615
复制相似问题