javavolatile
非java程序员,不过volatile在其他语言中也存在,简单说下。
1,volatile只在多线程程序中有意义。
2,为了提高性能,编译器工作时会进行一些优化,如指令排序,甚至跳过一些指令。如:
var
a=1;
a=2;
a=3;
编译后的结果可能就只执行
a
=
3
3,程序运行时,普通变量会有缓存机制(如cpu缓存、线程本地缓存等),程序读取时先从缓存读取,所以多线程的程序运行时可能存在脏读问题。即第一个线程已经修改了变量值,但第二个线程还在使用缓存中的旧数据。
volatile的作用就是告诉编译器,不要对使用该变量的代码进行优化,每次读写操作都访问变量的原始数据。
Ⅱ java中volatile使用的限制是什么
他的表述难以理解
你只要知道volatile 只能保证变量可见,不能保证原子性就行了,因为volatile 只有写内存屏蔽,没有读内存屏蔽
可见性:多线程需要读取volatile变量的值的时候不从本地内存读而从主内存读。
非原子性:所有线程对volatile 的访问可以是“同时”的。例如10个线程执行i++;可能有1~10个线程同时读取了i=2;这个值并做++,写回后是3(不是预期的值)。
Ⅲ java中对象或者数组用volatile修饰有什么用
就像大家更熟悉的const一样,volatile是一个类型修饰符(type specifier)。它是被设计用来修饰被不同线程访问和修改的变量。如果没有volatile,基本上会导致这样的结果:要么无法编写多线程程序,要么编译器失去大量优化的机会
volatile的作用是: 作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值.
Ⅳ java中volatile修饰的变量有什么特征
volatile具有可见性、有序性,不具备原子性。
注意,volatile不具备原子性,这是volatile与java中的synchronized、java.util.concurrent.locks.Lock最大的功能差异,这一点在面试中也是非常容易问到的点。
下面来分别看下可见性、有序性、原子性:
原子性:如果你了解事务,那这个概念应该好理解。原子性通常指多个操作不存在只执行一部分的情况,如果全部执行完成那没毛病,如果只执行了一部分,那对不起,你得撤销(即事务中的回滚)已经执行的部分。
可见性:当多个线程访问同一个变量x时,线程1修改了变量x的值,线程1、线程2...线程n能够立即读取到线程1修改后的值。
有序性:即程序执行时按照代码书写的先后顺序执行。在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。(本文不对指令重排作介绍,但不代表它不重要,它是理解JAVA并发原理时非常重要的一个概念)。
Ⅳ java volatile在什么场景下适用
JVM有两种运行模式Server与Client。两种模式的区别在于,Client模式启动速度较快,Server模式启动较慢;
但是启动进入稳定期长期运行之后Server模式的程序运行速度比Client要快很多。这是因为Server模式启动的
JVM采用的是重量级的虚拟机,对程序采用了更多的优化;而Client模式启动的JVM采用的是轻量级的虚拟机。
所以Server启动慢,但稳定后速度比Client远远要快。 64位系统的不支持Client 只支持server模式
server模式下 为了线程效率 线程会一直从私有堆栈中取值 公共堆栈修改后 不影响它,使用volatile修饰变量,可以强制
从公共堆栈中取值。
关键字volatile的使用场合是多个线程可以感知实例变量被更改,并且可以获取最新的值使用(线程私有内存和公共内存同步)。
但是它的缺点是不支持原子性。
Ⅵ java里volatile关键字有什么特性
Java语言中关键字 volatile 被称作轻量级的 synchronized,与synchronized相比,volatile编码相对简单且运行的时的开销较少,但能够正确合理的应用好 volatile 并不是那么的容易,因为它比使用锁更容易出错,接下来本文主要介绍 volatile 的使用准则,以及使用过程中需注意的地方。
为何使用volatile?
(1)简易性:在某些需要同步的场景下使用volatile变量要比使用锁更加简单
(2)性能:在某些情况下使用volatile同步机制的性能要优于锁
(3)volatile操作不会像锁一样容易造成阻塞
volatile特性
(1)volatile 变量具有 synchronized 的可见性特性,及如果一个字段被声明为volatile,java线程内存模型确保所有的线程看到这个变量的值是一致的
(2)禁止进行指令重排序
(3)不保证原子性
注:
① 重排序:重排序通常是编译器或运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段
② 原子性:不可中断的一个或一系列操作
③ 可见性:锁提供了两种主要特性:互斥和可见性,互斥即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调访问协议,这样,一次就只有一个线程能够使用该共享数据。可见性要更加复杂一些,它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的。
volatile的实现原理
如果对声明了volatile的变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,该Lock指令会使这个变量所在缓存行的数据回写到系统内存,根据缓存一致性协议,每个处理器都会通过嗅探在总线上传输的数据来检查自己缓存的值是否已过期,当处理器发现自己的缓存行对应的地址被修改,就会将当前处理器的缓存行设置成无效状态,在下次访问相同内存地址时,强制执行缓存行填充。
正确使用volatile的场景
volatile 主要用来解决多线程环境中内存不可见问题。对于一写多读,是可以解决变量同步问题,但是如果多写,就无法解决线程安全问题。如:
1、不适合使用volatile的场景(非原子性操作)
(1)反例
private static volatile int nextSerialNum = 0;
public static long generateSerialNum() {
return nextSerialNum++;
}
这个方法的目的是要确保每次调用都返回不同的自增值,然而结果并不理想,问题在于增量操作符(++)不是原子操作,实际上它是一个由读取-修改-写入操作序列组成的组合操作,如果第二个线程在第一个线程读取旧值和写回新值期间读取这个域,第二个线程与第一个线程就会读取到同一个值。
(2)正例
其实面对上面的反例场景可以使用JDK1.5 java.util.concurrent.atomic中提供的原子包装类型来保证原子性操作
private static AtomicInteger nextSerialNum = new AtomicInteger(0);
public static long generateSerialNum() {
return nextSerialNum.getAndIncrement();
}
2、适合使用volatile的场景
在日常工作当中volatile大多被在状态标志的场景当中,如:
要通过一个线程来终止另外一个线程的场景
(1)反例
private static boolean stopThread;
public static void main(String[] args) throws InterruptedException {
Thread th = new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while (!stopThread) {
i++;
}
}
});
th.start();
TimeUnit.SECONDS.sleep(2);
stopThread = true;
}
运行后发现该程序根本无法终止循环,原因是,java语言规范并不保证一个线程写入的值对另外一个线程是可见的,所以即使主线程main函数修改了共享变量stopThread状态,但是对th线程并不一定可见,最终导致循环无法终止。
(2)正例
private static volatile boolean stopThread;
public static void main(String[] args) throws InterruptedException {
Thread th = new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while (!stopThread) {
i++;
}
}
});
th.start();
TimeUnit.SECONDS.sleep(2);
stopThread = true;
}
通过使用关键字volatile修饰共享变量stopThread,根据volatile的可见性原则可以保证主线程main函数修改了共享变量stopThread状态后对线程th来说是立即可见的,所以在两秒内线程th将停止循环。
Ⅶ java的volatile是什么意思
volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。
这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。
而volatile关键字就是提示vm:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。
由于使用volatile屏蔽掉了vm中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。
就跟c中的一样
禁止编译器进行优化~~~~
Ⅷ java中的volatile关键是什么作用怎么使它
volatile关键字有什么用?
恐怕比较一下volatile和synchronized的不同是最容易解释清楚的。volatile是变量修饰符,而synchronized则作用于一段代码或方法;看如下三句get代码:
int i1; int geti1() {return i1;}
volatile int i2; int geti2() {return i2;}
int i3; synchronized int geti3() {return i3;}
geti1()得到存储在当前线程中i1的数值。多个线程有多个i1变量拷贝,而且这些i1之间可以互不相同。换句话说,另一个线程可能已经改变了它线程内的i1值,而这个值可以和当前线程中的i1值不相同。事实上,Java有个思想叫“主”内存区域,这里存放了变量目前的“准确值”。每个线程可以有它自己的变量拷贝,而这个变量拷贝值可以和“主”内存区域里存放的不同。因此实际上存在一种可能:“主”内存区域里的i1值是1,线程1里的i1值是2,线程2里的i1值是3——这在线程1和线程2都改变了它们各自的i1值,而且这个改变还没来得及传递给“主”内存区域或其他线程时就会发生。
而geti2()得到的是“主”内存区域的i2数值。用volatile修饰后的变量不允许有不同于“主”内存区域的变量拷贝。换句话说,一个变量经volatile修饰后在所有线程中必须是同步的;任何线程中改变了它的值,所有其他线程立即获取到了相同的值。理所当然的,volatile修饰的变量存取时比一般变量消耗的资源要多一点,因为线程有它自己的变量拷贝更为高效。
既然volatile关键字已经实现了线程间数据同步,又要synchronized干什么呢?呵呵,它们之间有两点不同。首先,synchronized获得并释放监视器——如果两个线程使用了同一个对象锁,监视器能强制保证代码块同时只被一个线程所执行——这是众所周知的事实。但是,synchronized也同步内存:事实上,synchronized在“主”内存区域同步整个线程的内存。因此,执行geti3()方法做了如下几步:
1. 线程请求获得监视this对象的对象锁(假设未被锁,否则线程等待直到锁释放)
2. 线程内存的数据被消除,从“主”内存区域中读入(Java虚拟机能优化此步。。。[后面的不知道怎么表达,汗])
3. 代码块被执行
4. 对于变量的任何改变现在可以安全地写到“主”内存区域中(不过geti3()方法不会改变变量值)
5. 线程释放监视this对象的对象锁
因此volatile只是在线程内存和“主”内存间同步某个变量的值,而synchronized通过锁定和解锁某个监视器同步所有变量的值。显然synchronized要比volatile消耗更多资源。
Ⅸ volatile关键字在Java中有什么作用
volatile 是把变量标识为“变化中的”。
意思是这个变量即使在(主线程)没有任何存取操作的情况下也可能在(被其他线程)变化。
写上提醒注意线程安全。Java 1.4及之前版本加了这个的变量也没有同步安全。Java 5以后加了volatile后会在读取方面有同步安全。
Ⅹ java volatile变量为什么不能保证原子性
被volatile修饰的变量保证的是可见性,不是原子性。这是两个不同的概念。
可见性是指不论在哪个线程中看,同一个对象同一时刻的值总是一样的,不会出现不一致的情况。
原子性是指一个操作要么完成,要么没有完成,不会出现完成了一半的情况。