Java中 Volatile关键字
Volatile关键字用于通过不同的线程修改变量的值。它还用于使类线程安全。这意味着多个线程可以同时使用类的方法和实例,而不会出现任何问题。 volatile关键字可以与原始类型或对象一起使用。
volatile关键字不缓存变量的值,并且始终从主内存中读取变量。 volatile关键字不能与类或方法一起使用。但是,它与变量一起使用。它还保证可见性和顺序。它可以防止编译器对代码进行重新排序。
特定设备寄存器的内容可以随时更改,因此您需要volatile关键字,以确保编译器不会优化此类访问。
示例
class Test
{
static int var=5;
}
在上面的示例中,假定两个线程在同一类上工作。两个线程都在不同的处理器上运行,其中每个线程都有其本地var副本。如果有任何线程修改其值,则更改将不会反映在主内存中的原始线程中。导致数据不一致,因为另一个线程不知道修改后的值。
class Test
{
static volatile int var =5;
}
在上面的示例中,静态变量是在所有对象之间共享的类成员。主存储器中只有一个副本。 volatile变量的值永远不会存储在缓存中。所有读写操作都将在主内存中进行。
何时使用它?
如果要自动读写long和double变量,可以使用volatile变量。
它可以用作Java中实现同步的另一种方法。
完成读取操作后,所有读取器线程将看到volatile变量的更新值。如果您不使用volatile关键字,则不同的阅读器线程可能会看到不同的值。
它用于通知编译器多个线程将访问特定的语句。这样可以防止编译器进行任何重新排序或任何优化。
如果不使用易失性变量,编译器可以对代码重新排序,可以自由地写入易失性变量的缓存值,而不用从主存储器中读取。
重要要点
您可以将volatile关键字与变量一起使用。在类和方法中使用volatile关键字是非法的。
它保证volatile变量的值始终从主内存中读取,而不是从本地线程缓存中读取。
如果您声明变量为volatile,则读写是原子的
它降低了内存一致性错误的风险。
任何对Java中的volatile变量的写操作都会在与该变量连续读取的关系建立之前发生。
volatile变量始终对其他线程可见。
作为对象引用的volatile变量可以为null。
在多个线程之间不共享变量时,不需要在该变量中使用volatile关键字。
同步与volatile关键字之间的区别
Volatile关键字不能替代synced关键字,但是在某些情况下可以用作替代。有以下区别如下:
Volatile关键字 |
Synchronization关键字 |
Volatile关键字是字段修饰符。 |
Synchronized关键字修改代码块和方法。 |
在发生波动的情况下,无法阻止线程等待。 |
在同步的情况下可以阻止线程等待。 |
它提高了线程性能。 |
同步方法会降低线程性能。 |
它一次在线程内存和主内存之间同步一个变量的值。 |
它在线程内存和主内存之间同步所有变量的值。 |
易失性字段不受编译器优化的影响。 |
同步需要进行编译器优化。 |
易失关键字示例
在下面的示例中,我们定义了一个增加计数器值的类。当线程开始执行时,VolatileThread.java中的run()方法将获取更新值和旧值。在主类中,我们定义线程的数组。
VolatileData.java
public class VolatileData{
private volatile int counter = 0;
public int getCounter(){
return counter;
}
public void increaseCounter() {
++counter;
//increases the value of counter by 1
}
}
VolatileThread.java
VolatileThread.java
public class VolatileThread extends Thread{
private final VolatileData data;
public VolatileThread(VolatileData data) {
this.data = data;
}
@Override
public void run(){
int oldValue = data.getCounter();
System.out.println("[Thread " + Thread.currentThread().getId() + "]: Old value = " + oldValue);
data.increaseCounter();
int newValue = data.getCounter();
System.out.println("[Thread " + Thread.currentThread().getId() + "]: new value = " + newValue);
}
}
VolatileMain.java
public class VolatileMain{
private final static int noOfThreads = 2;
public static void main(String[] args) throws InterruptedException{
VolatileData volatileData = new VolatileData();
//object of VolatileData class
Thread[] threads = new Thread[noOfThreads];
//creating Thread array
for(int i = 0;i <noOfThreads;++i)
threads[i] = new VolatileThread(volatileData);
for(int i = 0;i <noOfThreads;++i)
threads[i].start();
//starts all reader threads
for(int i = 0;i <noOfThreads;++i)
threads[i].join();
//wait for all threads
}
}
输出:
[Thread 9]: Old value = 0
[Thread 9]: new value = 1
[Thread 10]: Old value = 1
[Thread 10]: new value = 2