我试图了解volatile
在这里是如何运作的。如果我将cc
声明为volatile
,我会得到以下输出。我知道线程执行输出会不时变化,但我读到volatile
与synchronized
相同的地方,为什么我得到这个输出?如果我使用Thread1
的两个实例那么重要吗?
2Thread-0 2Thread-1 4Thread-1 3Thread-0 5Thread-1 6Thread-0 7Thread-1 8Thread-0 9Thread-1 10Thread-0 11Thread-1 12Thread-0
public class Volexample {
int cc=0;
public static void main(String[] args) {
Volexample ve=new Volexample();
CountClass count =ve.new CountClass();
Thread1 t1=ve.new Thread1(count);
Thread2 t2=ve.new Thread2(count);
t1.start();
t2.start();
}
class Thread1 extends Thread{
CountClass count =new CountClass();
Thread1(CountClass count ){
this.count=count;
}
@Override
public void run() {
/*for(int i=0;i<=5;i++)
count.countUp();*/
for(int i=0;i<=5;i++){
cc++;
System.out.println(cc + Thread.currentThread().getName());
}
}
}
class Thread2 extends Thread {
CountClass count =new CountClass();
Thread2(CountClass count ){
this.count=count;
}
@Override
public void run(){
/*for(int i=0;i<=5;i++)
count.countUp();*/
for(int i=0;i<=5;i++){
cc++;
System.out.println(cc + Thread.currentThread().getName());
}
}
}
class CountClass{
volatile int count=0;
void countUp(){
count++;
System.out.println(count + Thread.currentThread().getName());
}
}
}
答案 0 :(得分:5)
在Java中,volatile
关键字的语义定义非常明确。它们确保其他线程将看到变量的最新更改。但它们不会使读取 - 修改 - 写入操作成为原子。
因此,如果i
为volatile
并且您i++
,则可以保证读取i
的最新值,并保证其他线程会看到您的i
立即写入i
,但不能保证两个线程不会交错读取/修改/写入操作,这样两个增量就会产生单个增量的效果。
假设i++;
是一个易失整数,其值初始化为零,除此之外没有发生任何写操作,两个线程执行i
,可能发生以下情况:
i
的最新值。i
的最新值。i
。i
。i
的最新值是1,因此任何访问volatile
的线程现在都会看到一个。请注意,即使每个线程始终读取任何其他线程写入的最新值,也会丢失增量。 synchronized
关键字提供可见性,而非原子性。
您可以使用Atomic*
来形成复杂的原子操作。如果您只需要简单的,可以使用Java提供的各种SELECT field_b
FROM table
GROUP BY some_func(field_a) AS field_b
类。
答案 1 :(得分:0)
Java volatile
关键字用于将Java变量标记为“存储在主存储器中”。这意味着,每次读取一个volatile变量都将从计算机的主内存中读取,而不是从缓存中读取,并且每次写入volatile变量都将写入主内存,而不仅仅是缓存。
它保证您正在访问此变量的最新值。
P上。 S.使用更大的循环来发现错误。例如,尝试迭代10e9次。
答案 2 :(得分:0)
使用volatile的用例是从映射到设备寄存器的内存中读取/写入,例如在微控制器上,除了CPU之外的其他内容将读取/写入该“内存”地址的值所以编译器不应该优化该变量。