我在Oracle docs读到:
- 对于参考变量和大多数来说,读取和写入都是原子 原始变量(除long和double之外的所有类型)。
(我想这个功能已经在一些新的JDK版本中添加了,因为我曾经认为所有原始变量的读/写都不是原子的)
这是否意味着message Alarm {
uint32 hour = 1;
uint32 minute = 2;
bool repeat = 3;
DaysOfWeek daysOfWeek = 4;
message DaysOfWeek {
bool sunday = 1;
bool monday = 2;
bool tuesday = 3;
bool wednesday = 4;
bool thursday = 5;
bool friday = 6;
bool saturday = 7;
}
}
已被弃用且不应在新项目中使用?
答案 0 :(得分:16)
虽然来自普通#selector
的单个存储或单个加载在Java中是原子的,但您不能原子地(例如)递增它。这样做需要先加载值,然后根据它计算新值,然后再存储新值。但是在两次访问之间,另一个线程可能已经修改了该值。 int
提供getAndIncrement
之类的操作,无需使用锁即可用于此目的。
答案 1 :(得分:7)
弃用?一点也不。虽然原始变量的单独读取和写入是原子的,但AtomicInteger
(以及java.util.concurrent.atomic
中的其他原子类)提供了更复杂的操作,这些操作也是原子的。这些包括addAndGet(int)
之类的东西,它们对原始int
变量完全不是原子的。因此,
int i = 3;
AtomicInteger j = new AtomicInteger(3);
i += 5; // NOT thread-safe -- might not set i to 8
int n = j.addAndGet(5); // thread-safe -- always sets n to 8
(上述两条评论均假设i
和j
在问题开始执行时没有更改,但可能会被更改执行开始后但在完成之前的另一个线程。)
答案 2 :(得分:5)
是否意味着AtomicInteger已被弃用且不应在新项目中使用?
没有。首先也是最明显的,如果它被弃用,它将被标记为这样。
此外,AtomicInteger和原始int根本不可互换。有很多不同之处,但这里有前三个想到的:
compareAndSet()
。 AtomicInteger.getAndIncrement()
与int++
也不同;前者是原子的,第二种是两种非原子的操作。我想这个功能已经在一些新的JDK版本中添加了,因为我曾经认为所有原始变量的读/写都不是原子的
读取和写入32位或更小的基元始终是原子的。
答案 3 :(得分:3)
其他答案解决了为什么需要AtomicInteger
。我想澄清一下该文件的内容。
在该文档中使用术语 atomic 与AtomicInteger
中使用 atomic 的目的不同。
该文件还说明了
原子动作不能交错,因此可以毫无顾虑地使用它们 线程干扰。
这是指
int x;
x = 1; // thread 1
x = 2; // thread 2
System.out.println(x); // thread 3
thread 3
保证会看到值1
或值2
。
但是,对于long
或double
,您没有这种保证。 Java Language Specification州
出于Java编程语言内存模型的目的,a 单个写入非易失性
long
或double
值被视为两个 单独写入:每个32位一半。这可能导致a 线程看到64位值的前32位的情况 一次写入,另一次写入的第二次32位。
所以,例如,
long x;
x = 0xffff_ffffL; // thread 1
x = 0x7fff_ffff_0000_0000L; // thread 2
System.out.println(x); // thread 3
允许 thread 3
查看thread 1
分配的前32位和thread 2
分配的最后32位,创建long
1}}值7fff_ffff_ffff_ffff
。 double
也可能出现同样的情况。
使用long
修改double
或volatile
变量会阻止此行为。