`

java中关键字volatile的作用

阅读更多
用在多线程,同步变量。 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B。只在某些动作时才进行A和B的同步。因此存在A和B不一致的情况。volatile就是用来避免这种情况的。volatile告诉jvm, 它所修饰的变量不保留拷贝,直接访问主内存中的(也就是上面说的A)

=========================分割线1=================================

版权声明 :转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://aleung.blogbus.com/logs/32090434.html

在Java内存模型中,有main memory,每个线程也有自己的memory (例如寄存器)。为了性能,一个线程会在自己的memory中保持要访问的变量的副本。这样就会出现同一个变量在某个瞬间,在一个线程的memory中的值可能与另外一个线程memory中的值,或者main memory中的值不一致的情况。

一个变量声明为volatile,就意味着这个变量是随时会被其他线程修改的,因此不能将它cache在线程memory中。以下例子展现了volatile的作用:
public class StoppableTask extends Thread {

  private volatile boolean pleaseStop;


  public void run() {

    while (!pleaseStop) {

     // do some stuff...

    }

 }


  public void tellMeToStop() {

   pleaseStop = true;

  }

}

假如pleaseStop没有被声明为volatile,线程执行run的时候检查的是自己的副本,就不能及时得知其他线程已经调用tellMeToStop()修改了pleaseStop的值。

Volatile一般情况下不能代替sychronized,因为volatile不能保证操作的原子性,即使只是i++,实际上也是由多个原子操作组成:read i; inc; write i,假如多个线程同时执行i++,volatile只能保证他们操作的i是同一块内存,但依然可能出现写入脏数据的情况。如果配合Java 5增加的atomic wrapper classes,对它们的increase之类的操作就不需要sychronized。

Reference:
http://www.javamex.com/tutorials/synchronization_volatile.shtml
http://www.javamex.com/tutorials/synchronization_volatile_java_5.shtml
http://www.ibm.com/developerworks/cn/java/j-jtp06197.html
=========================分割线2=================================

  恐怕比较一下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消耗更多资源。

=========================分割线3=================================

volatile关键字相信了解Java多线程的读者都很清楚它的作用。volatile关键字用于声明简单类型变量,如int、float、 boolean等数据类型。如果这些简单数据类型声明为volatile,对它们的操作就会变成原子级别的。但这有一定的限制。例如,下面的例子中的n就不是原子级别的:

package  mythread;

public   class  JoinThread  extends  Thread
{
     public   static volatile int  n  =   0 ;
    public   void  run()
    {
         for  ( int  i  =   0 ; i  <   10 ; i ++ )
             try 
        {
                n  =  n  +   1 ;
                sleep( 3 );  //  为了使运行结果更随机,延迟3毫秒 

            }
             catch  (Exception e)
            {
            }
    }

     public   static   void  main(String[] args)  throws  Exception
    {

        Thread threads[]  =   new  Thread[ 100 ];
         for  ( int  i  =   0 ; i  <  threads.length; i ++ )
             //  建立100个线程 
            threads[i]  =   new  JoinThread();
         for  ( int  i  =   0 ; i  <  threads.length; i ++ )
             //  运行刚才建立的100个线程 
            threads[i].start();
         for  ( int  i  =   0 ; i  <  threads.length; i ++ )
             //  100个线程都执行完后继续 
            threads[i].join();
        System.out.println( " n= "   +  JoinThread.n);
    }
} 
   
如果对n的操作是原子级别的,最后输出的结果应该为n=1000,而在执行上面积代码时,很多时侯输出的n都小于1000,这说明n=n+1不是原子级别的操作。原因是声明为volatile的简单变量如果当前值由该变量以前的值相关,那么volatile关键字不起作用,也就是说如下的表达式都不是原子操作:

n  =  n  +   1 ;
n ++ ;

      如果要想使这种情况变成原子操作,需要使用synchronized关键字,如上的代码可以改成如下的形式:

package  mythread;

public   class  JoinThread  extends  Thread
{
     public   static int  n  =   0 ;

     public static   synchronized   void  inc()
    {
        n ++ ;
    }
     public   void  run()
    {
         for  ( int  i  =   0 ; i  <   10 ; i ++ )
             try 
            {
                inc();  //  n = n + 1 改成了 inc(); 
                sleep( 3 );  //  为了使运行结果更随机,延迟3毫秒 

            }
             catch  (Exception e)
            {
            }
    }

     public   static   void  main(String[] args)  throws  Exception
    {

        Thread threads[]  =   new  Thread[ 100 ];
         for  ( int  i  =   0 ; i  <  threads.length; i ++ )
             //  建立100个线程 
            threads[i]  =   new  JoinThread();
         for  ( int  i  =   0 ; i  <  threads.length; i ++ )
             //  运行刚才建立的100个线程 
            threads[i].start();
         for  ( int  i  =   0 ; i  <  threads.length; i ++ )
             //  100个线程都执行完后继续 
            threads[i].join();
        System.out.println( " n= "   +  JoinThread.n);
    }
} 

    上面的代码将n=n+1改成了inc(),其中inc方法使用了synchronized关键字进行方法同步。因此,在使用volatile关键字时要慎重,并不是只要简单类型变量使用volatile修饰,对这个变量的所有操作都是原来操作,当变量的值由自身的上一个决定时,如n=n+1、n++ 等,volatile关键字将失效,只有当变量的值和自身上一个值无关时对该变量的操作才是原子级别的,如n = m + 1,这个就是原级别的。所以在使用volatile关键时一定要谨慎,如果自己没有把握,可以使用synchronized来代替volatile。



本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/orzorz/archive/2009/07/03/4319055.aspx
分享到:
评论
17 楼 virtualspider 2018-10-18  
请问我用了volatile不copy到线程内存里面的话,我怎么i++?多个线程同时执行read i,increate,write i。这些数据是存在哪里???
16 楼 saiarmuluo 2016-11-07  
不错,最近在看并发的知识一直没弄懂这块,谢谢!
15 楼 mao_siyu 2016-07-04  
多谢楼主 讲的很明白~~
14 楼 qiangcz 2016-05-23  
angole 写道
jgroups 写道
volatile告诉jvm, 它所修饰的变量不保留拷贝,直接访问主内存中的(也就是上面说的A)

这里volatile到底是不用拷贝的B,直接访问A,还是保证访问B时B的值是最新的A?????
一直没有一个明确的答案?有没有人知道,忘回复!!!


同问!

不拷贝,直接访问
12 楼 angole 2015-01-27  
jgroups 写道
volatile告诉jvm, 它所修饰的变量不保留拷贝,直接访问主内存中的(也就是上面说的A)

这里volatile到底是不用拷贝的B,直接访问A,还是保证访问B时B的值是最新的A?????
一直没有一个明确的答案?有没有人知道,忘回复!!!


同问!
11 楼 jgroups 2014-09-19  
josico 写道
楼主,问一下,看是我理解的有问题,还是说我代码写错了。
我没加volatile,线程也仍然停了,我模拟不出反应加和不加volatile具体区别的实例出来
public class Client {

	public static void main(String[] args) {
		Thread1 t1 = new Thread1();
		t1.start();
		t1.plzStop();
	}

}

class Thread1 extends Thread {
	
	private boolean flag = true;

	@Override
	public void run() {
		int i = 0;
		while (flag) {
			i++;
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			if (i > 9) {
				flag = false;
			}
		}
	}
	
	public void plzStop(){
		flag = false;
	}
}

是不是欣慰start方法还没有执行完,plzStop就已经执行了
10 楼 jgroups 2014-09-19  
volatile告诉jvm, 它所修饰的变量不保留拷贝,直接访问主内存中的(也就是上面说的A)

这里volatile到底是不用拷贝的B,直接访问A,还是保证访问B时B的值是最新的A?????
一直没有一个明确的答案?有没有人知道,忘回复!!!
9 楼 sy888goodboy 2014-08-21  
不错,学习了!
8 楼 josico 2014-04-28  
楼主,问一下,看是我理解的有问题,还是说我代码写错了。
我没加volatile,线程也仍然停了,我模拟不出反应加和不加volatile具体区别的实例出来
public class Client {

	public static void main(String[] args) {
		Thread1 t1 = new Thread1();
		t1.start();
		t1.plzStop();
	}

}

class Thread1 extends Thread {
	
	private boolean flag = true;

	@Override
	public void run() {
		int i = 0;
		while (flag) {
			i++;
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			if (i > 9) {
				flag = false;
			}
		}
	}
	
	public void plzStop(){
		flag = false;
	}
}
7 楼 wankunde 2013-12-24  
seashoreliu 写道
楼主,如果我main里面这样写public static void main(String[] args) throws Exception {

Thread threads[] = new Thread[100];
for (int i = 0; i < threads.length; i++)
// 建立100个线程
threads[i] = new JoinThread();
for (int i = 0; i < threads.length; i++)
// 运行刚才建立的100个线程
new JoinThread().start();
for (int i = 0; i < threads.length; i++)
// 100个线程都执行完后继续
new JoinThread().join();
System.out.println(" n= " + JoinThread.n);
}运行结果就不对了,即时加上synchrozied也不行,能解释一下么



测试发现,楼主的理论是对的,就是操作一定要是原子级的,但是代码解释不对!

inc方法必须要加static synchronized 两个关键字,才可以保证操作的原子级。
区别:
非static方法直接由线程调用:
  1. 操作的数据先由class field 拷贝到instance field
  2. 调用inc函数累加
  3. 复制n副本到class field
那么在操作inc的时候,其他线程可能也已经完成1操作,导致数据不一致。虽然加了volatile,数据拷贝的时候是一致的,但是inc后就不一致了。
static方法直接由线程调用:
  1. 线程直接寻址到class method处执行原子函数 inc
加入synchronized保证函数的顺序执行。

这是我的理解,但是楼主说synchronized可以自动拷贝数据副本,这个按道理来说应该可以解决非static方法的问题才对,搞不懂。。。
6 楼 haanya168 2013-12-23  
讲得很好,谢谢,通俗易懂!
5 楼 seashoreliu 2013-11-29  
楼主,如果我main里面这样写public static void main(String[] args) throws Exception {

Thread threads[] = new Thread[100];
for (int i = 0; i < threads.length; i++)
// 建立100个线程
threads[i] = new JoinThread();
for (int i = 0; i < threads.length; i++)
// 运行刚才建立的100个线程
new JoinThread().start();
for (int i = 0; i < threads.length; i++)
// 100个线程都执行完后继续
new JoinThread().join();
System.out.println(" n= " + JoinThread.n);
}运行结果就不对了,即时加上synchrozied也不行,能解释一下么
4 楼 jateide 2013-11-14  
引用
在使用volatile关键字时要慎重,并不是只要简单类型变量使用volatile修饰,对这个变量的所有操作都是原来操作,当变量的值由自身的上一个决定时,如n=n+1、n++ 等,volatile关键字将失效,只有当变量的值和自身上一个值无关时对该变量的操作才是原子级别的,如n = m + 1,这个就是原级别的。

这个讲解的很清楚,需要认清对变量的操作是否是原子操作。
3 楼 AKka 2013-10-08  
说得很清楚,受教了!3Q
2 楼 flora3000 2013-05-10  
讲的很明白~
1 楼 yzhw 2010-12-09  
正需要,刚好遇到一个多线程定时任务的问题

相关推荐

    java volatile 关键字实战

    java volatile 关键字实战java volatile 关键字实战java volatile 关键字实战java volatile 关键字实战java volatile 关键字实战java volatile 关键字实战java volatile 关键字实战java volatile 关键字实战java ...

    Java关键字volatile和synchronized作用和区别

    Java关键字volatile和synchronized是Java语言中两个常用的关键字,它们都是用来解决多线程编程中的同步问题的,但它们的作用和用法却有所不同。 volatile关键字是变量修饰符,它的作用是确保变量在多个线程之间的...

    java volatile 关键字 学习

    java volatile 关键字 学习

    深入理解Java中的volatile关键字(总结篇)

    深入理解Java中的volatile关键字 Java中的volatile关键字是一个非常重要的概念,它不仅仅在Java语言中有,在很多语言中都有的,而且其用法和语义也都是不尽相同的。volatile关键字的主要作用是提供可见性、原子性和...

    Java 关键字 volatile 的理解与正确使用

    Java 语言中关键字 volatile 被称作轻量级的 synchronized,与 synchronized 相比,volatile 编码相对简单且运行时的开销较少,但能够正确合理地应用好 volatile 并不是那么的容易,因为它比使用锁更容易出错。...

    Java中的关键字volatile详解

    Java中的关键字volatile详解 一、volatile关键字原理 在Java中,volatile关键字经常用来修饰变量。volatile关键字的作用是使变量在多个线程之间可见。volatile关键字可以保证变量的可见性,但不能保证变量的原子性...

    一文精通Java中的volatile关键字

    Java中的`volatile`关键字是多线程编程中的一个重要概念,它的主要作用是确保共享变量的可见性和禁止指令重排序。本文将深入探讨`volatile`的关键特性、工作原理以及使用注意事项。 1. 可见性: `volatile`关键字...

    java里的volatile关键字详解

    "Java中的Volatile关键字详解" Java中的Volatile关键字详解是Java中的一种关键字,用于保证线程之间的可见性、原子性和有序性。下面是对Java中的Volatile关键字详解的知识点总结: 一、基本概念 1. 可见性:可见...

    Java线程:volatile关键字

    Java 线程 volatile 关键字详解 Java™ 语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量。volatile 变量的同步性较差,但它有时更简单并且开销更低。volatile 变量可以被看作是一种 “程度较轻的 ...

    解析java中volatile关键字

    Java中的`volatile`关键字是多线程编程中的一个重要概念,它的主要作用是解决并发环境下的可见性和有序性问题。在Java内存模型(JMM)中,每个线程都有自己的工作内存,其中包含了线程对共享变量的副本。线程间的...

    Java中volatile关键字的作用与用法详解

    Java中的`volatile`关键字是一个非常重要的并发编程工具,它的作用主要体现在两个方面:**可见性**和**有序性**。本文将深入解析`volatile`的关键字特性及其在实际编程中的应用。 1. 可见性: 当一个共享变量被`...

    Java并发编程:volatile关键字解析

    总结来说,`volatile`关键字是Java并发编程中一个非常重要的概念。它不仅解决了多线程环境下的可见性问题,还能够在一定程度上保证有序性。然而,它并不保证复合操作的原子性,因此在设计并发程序时,还需要根据具体...

    Java的关键字与保留字

    Java拥有51个关键字,这些关键字在编程中扮演着核心角色,用于声明类型、控制流程、异常处理等。下面我们将逐一介绍这些关键字: 1. **abstract**:用于定义抽象类和抽象方法,表示该类不能被实例化,或方法没有...

    Java中volatile关键字的含义

    在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候可以万事大吉。  Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 ...

    java语言的volatile教程,java语言的volatile关键字是干什么用的

    java语言的volatile教程,java语言的volatile关键字到底怎么用

    Java基础之关键字_Java基础之关键字_源码

    在编程语言的学习中,了解和掌握关键字是至关重要的一步,特别是在Java这种强类型的语言中。本文将深入探讨Java中的关键字,这些关键字是Java语法结构的基础,它们被Java编译器特殊处理,有着特定的含义和功能。 ...

    多方面解读Java中的volatile关键字.rar

    在Java编程语言中,`volatile`关键字是一个非常关键的同步原语,用于处理并发编程中的可见性和有序性问题。这个关键字对于理解Java内存模型(JMM)以及如何编写线程安全的代码至关重要。下面我们将从多个角度深入...

    java中的关键字大全

    根据给定的部分内容,我们可以列出以下Java关键字: 1. **abstract** 2. **continue** 3. **for** 4. **new** 5. **switch** 6. **assert** 7. **default** 8. **if** 9. **package** 10. **synchronized** 11. **...

    Java中volatile关键字的总结.docx

    `volatile`关键字的作用在于强制线程每次使用变量时都从主内存中读取最新的值,而不是依赖于本地内存的副本,这样确保了不同线程间的共享变量可见性。 ### 四、`volatile`关键字的限制与使用场景 虽然`volatile`...

    java关键字总结文档

    以下是对给定文件中提到的Java关键字进行的详细解释与总结: #### 1. `abstract` - **用途**:`abstract`关键字用于声明抽象类或抽象方法。抽象类不能被实例化,但可以被其他类继承;抽象方法没有具体实现,必须由...

Global site tag (gtag.js) - Google Analytics