java volatile关键字 深入理解
2023-12-27 22:57:05
简述特性
在Java中,volatile 是一个关键字,用于修饰类的实例变量。volatile 的主要作用是保证多个线程能够正确地处理被多个线程共享的变量。当一个变量被声明为volatile时,它具有以下两个特性:
- 可见性(Visibility): 当一个线程修改了volatile变量的值时,这个新值会立即被其他线程看到。这是因为volatile变量在主存中保存,每次使用之前都会从主存中读取最新的值,而不是使用线程本地的缓存。
- 禁止指令重排序(Atomicity): volatile关键字禁止指令重排序,确保对volatile变量的操作是原子的。这就意味着volatile变量的读和写是不可分割的操作,不会被其他线程的操作中断。
尽管volatile提供了一定程度的线程安全,但它并不能完全替代锁。volatile适用于那些变量的写操作不依赖于当前值的情况,例如标志位的修改。但对于一些复合操作,仍然需要使用锁来确保原子性。
需要注意的是,volatile并不能保证原子性,因此不能替代synchronized关键字。如果需要确保一系列操作的原子性,还是需要使用锁。
问题示例
示例一
public class NonVolatileExample {
private boolean flag = false;
public static void main(String[] args) {
NonVolatileExample example = new NonVolatileExample();
// 线程1:修改flag的值
Thread thread1 = new Thread(() -> {
try {
Thread.sleep(1000); // 模拟一些耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
example.setFlag(true);
System.out.println("Flag is set to true by Thread 1");
});
// 线程2:检查flag的值
Thread thread2 = new Thread(() -> {
while (!example.isFlag()) {
// 等待flag变为true
}
System.out.println("Flag is true, Thread 2 can proceed");
});
thread1.start();
thread2.start();
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
当运行上述示例,会在while (!example.isFlag())
处一直卡着,说明线程thread2读取flag一直读取的是旧值
示例二
public class NonVolatileExample {
private boolean flag = false;
public static void main(String[] args) {
NonVolatileExample example = new NonVolatileExample();
// 线程1:修改flag的值
Thread thread1 = new Thread(() -> {
try {
Thread.sleep(1000); // 模拟一些耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
example.setFlag(true);
System.out.println("Flag is set to true by Thread 1");
});
// 线程2:检查flag的值
Thread thread2 = new Thread(() -> {
while (!example.isFlag()) {
// 等待flag变为true
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Flag is true, Thread 2 can proceed");
});
thread1.start();
thread2.start();
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
当在线程thread2中加上Thread.sleep(1000)
就没有出现while中长时间等待的情况,这里说明thread2线程还是可以读取到thread1修改后的数据,只是什么时候放弃缓存读取主存的时机不由java程序控制,是由操作系统及计算机硬件决定,所以会导致示例一的情况,thread1修改过很长时间后thread2才能读取到共享对象
使用示例
public class NonVolatileExample {
private volatile boolean flag = false;
public static void main(String[] args) {
NonVolatileExample example = new NonVolatileExample();
// 线程1:修改flag的值
Thread thread1 = new Thread(() -> {
try {
Thread.sleep(1000); // 模拟一些耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
example.setFlag(true);
System.out.println("Flag is set to true by Thread 1");
});
// 线程2:检查flag的值
Thread thread2 = new Thread(() -> {
while (!example.isFlag()) {
// 等待flag变为true
}
System.out.println("Flag is true, Thread 2 can proceed");
});
thread1.start();
thread2.start();
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
加上volatile关键字后java就能控制读取主存的时机
文章来源:https://blog.csdn.net/qq_44272797/article/details/135256531
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!