Ric*_*ard 628 java multithreading volatile keyword
在今天的工作中,我遇到了volatileJava中的关键字.我不太熟悉它,我发现了这个解释:
鉴于该文章解释了相关关键字的详细信息,您是否使用过它,或者您是否曾经看到过以正确方式使用此关键字的情况?
Gre*_*tes 711
volatile具有内存可见性的语义.基本上,在volatile写入操作完成后,所有读取器(特别是其他线程)都可以看到字段的值.没有volatile,读者可以看到一些非更新的价值.
回答你的问题:是的,我使用一个volatile变量来控制某些代码是否继续循环.循环测试该volatile值并继续(如果是)true.可以false通过调用"停止"方法来设置条件.循环看到了false在stop方法完成执行后测试值时,并终止.
我强烈推荐的" Java Concurrency in Practice "这本书给出了很好的解释volatile.本书由撰写问题中引用的IBM文章的同一人撰写(事实上,他引用了该文章底部的书).我的用法volatile是他的文章称之为"模式1状态标志".
如果您想了解更多有关如何volatile工作的内容,请阅读Java内存模型.如果你想超越这个级别,请查看一本好的计算机体系结构书籍,如Hennessy和Patterson,并阅读缓存一致性和缓存一致性.
And*_*NER 170
"... volatile修饰符保证读取字段的任何线程都会看到最近写入的值." - Josh Bloch
如果您正在考虑使用volatile,请阅读java.util.concurrent处理原子行为的包.
关于Singleton模式的维基百科帖子显示了使用中的不稳定性.
Pre*_*raj 118
关键点volatile:
synchronized和volatile和锁.synchronized变量.将synchronized关键字与变量一起使用是非法的,将导致编译错误.synchronized您可以使用java volatile变量代替在Java中使用变量,该变量将指示JVM线程volatile从主内存读取变量的值,而不是在本地缓存它.volatile关键字.用法示例volatile:
public class Singleton {
private static volatile Singleton _instance; // volatile variable
public static Singleton getInstance() {
if (_instance == null) {
synchronized (Singleton.class) {
if (_instance == null)
_instance = new Singleton();
}
}
return _instance;
}
}
Run Code Online (Sandbox Code Playgroud)
我们正在第一次请求时懒洋洋地创建实例.
如果我们不创建_instance变量volatile,则创建实例的Thread Singleton无法与其他线程通信.因此,如果线程A正在创建Singleton实例,并且在创建之后,CPU会破坏等,所有其他线程将无法看到值为_instance非null,并且他们将认为它仍然被赋值为null.
为什么会这样?因为读取器线程没有进行任何锁定,并且在写入器线程从同步块中出来之前,内存将不会被同步,并且_instance将不会在主内存中更新值.使用Java中的Volatile关键字,这由Java本身处理,并且所有读取器线程都可以看到此类更新.
结论:
volatile关键字也用于在线程之间传递内存的内容.
不使用volatile的示例用法:
public class Singleton{
private static Singleton _instance; //without volatile variable
public static Singleton getInstance(){
if(_instance == null){
synchronized(Singleton.class){
if(_instance == null) _instance = new Singleton();
}
}
return _instance;
}
Run Code Online (Sandbox Code Playgroud)
上面的代码不是线程安全的.虽然它在synchronized块中再次检查实例的值(出于性能原因),但JIT编译器可以重新排列字节码,方式是在构造函数完成执行之前设置对实例的引用.这意味着方法getInstance()返回一个可能尚未完全初始化的对象.为了使代码具有线程安全性,可以使用关键字volatile,因为Java 5用于实例变量.标记为volatile的变量只有在对象的构造函数完全执行后才能对其他线程可见.
资源
volatile在Java中的用法:
快速失败的迭代器通常使用volatile列表对象上的计数器实现.
Iterator被创建时,计数器的当前值被嵌入Iterator对象.Iterator执行操作时,该方法比较两个计数器值并抛出ConcurrentModificationException它们是否不同.故障安全迭代器的实现通常是轻量级的.它们通常依赖于特定列表实现的数据结构的属性.没有一般模式.
Pyr*_*cal 50
volatile对于停止线程非常有用.
并不是说你应该编写自己的线程,Java 1.6有很多很好的线程池.但如果你确定需要一个线程,你需要知道如何阻止它.
我用于线程的模式是:
public class Foo extends Thread {
private volatile boolean close = false;
public void run() {
while(!close) {
// do work
}
}
public void close() {
close = true;
// interrupt here if needed
}
}
Run Code Online (Sandbox Code Playgroud)
注意不需要同步
Dav*_* L. 30
使用的一个常见示例volatile是使用volatile boolean变量作为标志来终止线程.如果你已经启动了一个线程,并且你希望能够安全地从另一个线程中断它,你可以让线程定期检查一个标志.要停止它,请将标志设置为true.通过创建标志volatile,您可以确保正在检查它的线程将在下次检查它时设置它,而不必使用synchronized块.
Sup*_*hne 19
用volatile关键字声明的变量有两个主要特性使它变得特殊.
如果我们有一个volatile变量,它就不能被任何线程缓存到计算机的(微处理器)高速缓存中.访问总是发生在主内存中.
如果对volatile变量进行写操作,并且突然请求读操作,则保证在读操作之前完成写操作.
以上两个品质推断出这一点
而另一方面,
volatile关键字是维护共享变量的理想方式,该共享变量具有'n'个读线程并且只有一个写线程来访问它.一旦我们添加了volatile关键字,就完成了.关于线程安全没有任何其他开销.Conversly,
我们不能volatile单独使用关键字来满足一个共享变量,该变量有多个写入线程访问它.
yka*_*ich 12
是的,只要您希望多个线程访问可变变量,就必须使用volatile.它不是很常见的用例,因为通常您需要执行多个单独的原子操作(例如,在修改之前检查变量状态),在这种情况下,您将使用synchronized块.
假设某个线程修改了共享变量的值(如果您没有volatile对该变量使用修饰符)。当其他线程想要读取该变量的值时,它们看不到更新后的值,因为它们从 CPU 的缓存而不是 RAM 内存中读取变量的值。此问题也称为Visibility Problem。
通过声明共享变量volatile,对计数器变量的所有写入都将立即写回主内存。此外,计数器变量的所有读取都将直接从主存储器读取。
public class SharedObject {
public volatile int sharedVariable = 0;
}
Run Code Online (Sandbox Code Playgroud)
对于非易失性变量,无法保证 Java 虚拟机 (JVM) 何时将数据从主内存读取到 CPU 缓存,或将数据从 CPU 缓存写入主内存。这可能会导致几个问题,我将在以下各节中解释这些问题。
例子:
想象一下这样的情况,两个或多个线程可以访问包含如下声明的计数器变量的共享对象:
public class SharedObject {
public int counter = 0;
}
Run Code Online (Sandbox Code Playgroud)
还可以想象一下,只有线程 1 递增计数器变量,但线程 1 和线程 2 都可能不时读取计数器变量。
如果计数器变量未声明为 易失性,则无法保证计数器变量的值何时从 CPU 缓存写回主内存。这意味着,CPU 缓存中的计数器变量值可能与主内存中的不同。这种情况如下所示:
由于变量尚未被另一个线程写回主内存,因此线程看不到变量的最新值的问题称为“可见性”问题。一个线程的更新对其他线程不可见。
volatile=> synchronized[关于]
volatile对于程序员来说,该值始终是最新的。问题是该值可以保存在不同类型的硬件内存中。例如它可以是 CPU 寄存器、CPU 缓存、RAM... ?PU 寄存器和 CPU 缓存属于 CPU,不能共享数据,不像 RAM 在多线程环境中救援
volatile关键字表示将直接从/向 RAM 内存读取和写入变量。它有一些计算足迹
Java 5volatile通过支持扩展happens-before[关于]
对 volatile 字段的写入发生在对该字段的每次后续读取之前。
Read is after write
Run Code Online (Sandbox Code Playgroud)
volatile关键字不能治愈一个race condition情况,当多个线程可以写同时一些值。答案是synchronized关键词[关于]
因此,仅当一个线程写入而其他线程仅读取该volatile值时才安全
如果您正在开发多线程应用程序,则需要使用'volatile'关键字或'synchronized'以及您可能拥有的任何其他并发控制工具和技术.这种应用的示例是桌面应用.
如果您正在开发将部署到应用程序服务器(Tomcat,JBoss AS,Glassfish等)的应用程序,则您不必自己处理并发控制,因为应用程序服务器已经解决了这一问题.事实上,如果我记得正确,Java EE标准禁止在servlet和EJB中进行任何并发控制,因为它是"基础"层的一部分,你应该放弃它来处理它.如果要实现单例对象,则只在此类应用程序中执行并发控制.如果您使用像Spring这样的框架编织组件,这甚至已经解决了.
因此,在Java开发的大多数情况下,应用程序是Web应用程序并使用IoC框架(如Spring或EJB),您不需要使用"volatile".
volatile只保证所有线程,甚至自己都在递增.例如:计数器同时看到变量的同一面.它不是用来代替同步或原子或其他东西,它完全使读取同步.请不要将它与其他java关键字进行比较.如下面的示例所示,易失性变量操作也是原子的,它们会立即失败或成功.
package io.netty.example.telnet;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static volatile int a = 0;
public static void main(String args[]) throws InterruptedException{
List<Thread> list = new ArrayList<Thread>();
for(int i = 0 ; i<11 ;i++){
list.add(new Pojo());
}
for (Thread thread : list) {
thread.start();
}
Thread.sleep(20000);
System.out.println(a);
}
}
class Pojo extends Thread{
int a = 10001;
public void run() {
while(a-->0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
Main.a++;
System.out.println("a = "+Main.a);
}
}
}
Run Code Online (Sandbox Code Playgroud)
即使你把挥发性或不结果总是不同的.但是如果您使用AtomicInteger,则结果将始终相同.这与同步也是一样的.
package io.netty.example.telnet;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
public static volatile AtomicInteger a = new AtomicInteger(0);
public static void main(String args[]) throws InterruptedException{
List<Thread> list = new ArrayList<Thread>();
for(int i = 0 ; i<11 ;i++){
list.add(new Pojo());
}
for (Thread thread : list) {
thread.start();
}
Thread.sleep(20000);
System.out.println(a.get());
}
}
class Pojo extends Thread{
int a = 10001;
public void run() {
while(a-->0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
Main.a.incrementAndGet();
System.out.println("a = "+Main.a);
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
243006 次 |
| 最近记录: |