相信各位朋友应该在项目中都有用到或者在别人项目中看到过volatile这个关键字,但是这个关键字具体是什么意思,有什么含义,本篇博客就带你来了解~
这里我先提前预告一下这个关键字的功能:可见性、禁止指令重排、不能保证操作的原子性
可见性
在java内存模型当中,当一个线程去读取一个变量的时候,首先会先从主内存当中读取并存入到线程本地缓存当中,这样如果该线程对变量进行了修改,其它线程来读取的时候,读取的变量就不是最新值
但是如果我使用了volatile这个关键字就不一样了,现在会直接读取主内存当中的变量并将修改后的变量值写入到主内存当中,这个时候如果有其他线程来读取这个变量的时候,就会强制读取主内存的变量值,保证了读取到的变量值是最新的变量值,这就保证了变量的可见性。
这里给一个演示代码可以看一下:
// 用 volatile 修饰,保证线程之间的可见性private static volatile boolean flag = true;public static void main(String[] args) throws InterruptedException {// 创建一个线程,不断检查 flag 的值Thread readerThread = new Thread(() -> {System.out.println("Reader thread started...");while (flag) {// 线程忙等待,检查 flag 值}System.out.println("Reader thread finished, flag changed to false.");});// 启动 reader 线程readerThread.start();// 主线程休眠一段时间,确保 reader 线程已启动Thread.sleep(2000);// 主线程修改 flag 的值System.out.println("Main thread changing flag to false.");flag = false;// 等待 reader 线程结束readerThread.join();System.out.println("Test completed.");}
reader线程在读取到主线程将变量值修改为false之后结束了自己~
禁止指令重排
编译器和处理器为了优化性能,可能会对指令进行重新编排,这个时候在单线程环境下是没有任何问题的,但是在多线程环境中就会出现变量没有初始化完成的问题,这里有一个典型的案例,java设计模式当中单例模式中处理线程不安全有个方法——懒汉模式,这里就用到了volatile这个关键字来处理指令重排的问题。先看一下代码:
public class SingleModelTest {private volatile static SingleModelTest instance;private SingleModelTest(){}public static SingleModelTest getInstance(){if(instance==null){synchronized (SingleModelTest.class){if(instance==null){instance=new SingleModelTest();}}}return instance;}
}
以上就是懒汉模式代码
在没有加这两行代码前,这个类会发生什么情况呢?
类被初始化多次:在我们没有加synchronized这个关键字的时候,就可能会发生一个线程在调用全局访问点进行初始化的时候,另一个线程进来也调用这个全局访问点来进行初始化,就有可能把这个类初始化多次,通过synchronized来控制方法块,就能保证这个类只会被初始化一次~
类没有完成初始化:在初始化过程中,jvm会执行以下初始化顺序命令:
1、初始化类 2、为这个类分配内存空间 3、将这个类指向内存空间
但是,由于编译器和处理器为了优化性能,所以会进行指令重排,就有可能在多线程环境下,可能这个类知识被初始化了,但是并没有被分配内存空间,但是此时instance已经不为空,所以也就不会执行里面的逻辑,所以这个时候就会出现问题。
好了,这个时候当我们在变量上加了volatile就不一样了,这个关键字可以帮我我们阻止指令重排,这样就可以有效的帮助我们完成类的初始化操作。
好了,听到这儿其实你已经了解volatile大概是怎么使用的了,当然同时你也了解了单例模式中懒汉模式这个代码为什么这么写,不能说完全让你听懂嘛,至少听个半懂是没问题的,毕竟再讲多一点儿这篇博客就要偏题了~
不能保证操作的原子性
就比如我们常见的一个count++操作,这个操作我们都知道不是原子操作,其中包含了多个步骤(读、加1、写)
public class AtomicityExample {private volatile int count = 0;public void increment() {count++; // 非原子操作}
}
- 在上例中,多个线程调用
increment()
方法时会出现线程安全问题,因为count++
不是原子操作。即使count
使用了volatile
修饰,仍然可能导致最终结果不一致。此类操作通常使用AtomicInteger
或synchronized
关键字来保证原子性。
好了,讲到这儿你应该对这个关键字或多或少都有了一个初步的了解了,希望看完这篇博客的你能有一定收获~