`
y806839048
  • 浏览: 1126458 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

volatile 和 synchronized区别

阅读更多

volatile 和 synchronized区别

 

1,volatile 只是单纯的同步主存区

2,volatile 执行没有顺序,不可避免脏读(自身变量的值决定本变量的值时n=n+1、n++ 等(自运算时))

3,都会同步主存区

 

 

用在多线程,同步变量。 线程为了提高效率,将某成员变量(如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的作用: 

Java代码  收藏代码
  1. public class StoppableTask extends Thread {  
  2.   
  3.   private volatile boolean pleaseStop;  
  4.   
  5.   
  6.   public void run() {  
  7.   
  8.     while (!pleaseStop) {  
  9.   
  10.      // do some stuff...  
  11.   
  12.     }  
  13.   
  14.  }  
  15.   
  16.   
  17.   public void tellMeToStop() {  
  18.   
  19.    pleaseStop = true;  
  20.   
  21.   }  
  22.   
  23. }  


假如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代码: 

Java代码  收藏代码
  1. int i1;               
  2. int geti1() {return i1;}   
  3. volatile int i2;   
  4. int geti2()  
  5. {return i2;}   
  6. int i3;                
  7. synchronized int geti3() {return i3;}   
  8.   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就不是原子级别的: 

Java代码  收藏代码
  1. package  mythread;  
  2.   
  3. public   class  JoinThread  extends  Thread  
  4. {  
  5.      public   static volatile int  n  =   0 ;  
  6.     public   void  run()  
  7.     {  
  8.          for  ( int  i  =   0 ; i  <   10 ; i ++ )  
  9.              try   
  10.         {  
  11.                 n  =  n  +   1 ;  
  12.                 sleep( 3 );  //  为了使运行结果更随机,延迟3毫秒   
  13.   
  14.             }  
  15.              catch  (Exception e)  
  16.             {  
  17.             }  
  18.     }  
  19.   
  20.      public   static   void  main(String[] args)  throws  Exception  
  21.     {  
  22.   
  23.         Thread threads[]  =   new  Thread[ 100 ];  
  24.          for  ( int  i  =   0 ; i  <  threads.length; i ++ )  
  25.              //  建立100个线程   
  26.             threads[i]  =   new  JoinThread();  
  27.          for  ( int  i  =   0 ; i  <  threads.length; i ++ )  
  28.              //  运行刚才建立的100个线程   
  29.             threads[i].start();  
  30.          for  ( int  i  =   0 ; i  <  threads.length; i ++ )  
  31.              //  100个线程都执行完后继续   
  32.             threads[i].join();  
  33.         System.out.println( " n= "   +  JoinThread.n);  
  34.     }  
  35. }   

    
如果对n的操作是原子级别的,最后输出的结果应该为n=1000,而在执行上面积代码时,很多时侯输出的n都小于1000,这说明n=n+1不是原子级别的操作。原因是声明为volatile的简单变量如果当前值由该变量以前的值相关,那么volatile关键字不起作用,也就是说如下的表达式都不是原子操作: 

n  =  n  +   1 ; 
n ++ ; 

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

Java代码  收藏代码
  1. package  mythread;  
  2.   
  3. public   class  JoinThread  extends  Thread  
  4. {  
  5.      public   static int  n  =   0 ;  
  6.   
  7.      public static   synchronized   void  inc()  
  8.     {  
  9.         n ++ ;  
  10.     }  
  11.      public   void  run()  
  12.     {  
  13.          for  ( int  i  =   0 ; i  <   10 ; i ++ )  
  14.              try   
  15.             {  
  16.                 inc();  //  n = n + 1 改成了 inc();   
  17.                 sleep( 3 );  //  为了使运行结果更随机,延迟3毫秒   
  18.   
  19.             }  
  20.              catch  (Exception e)  
  21.             {  
  22.             }  
  23.     }  
  24.   
  25.      public   static   void  main(String[] args)  throws  Exception  
  26.     {  
  27.   
  28.         Thread threads[]  =   new  Thread[ 100 ];  
  29.          for  ( int  i  =   0 ; i  <  threads.length; i ++ )  
  30.              //  建立100个线程   
  31.             threads[i]  =   new  JoinThread();  
  32.          for  ( int  i  =   0 ; i  <  threads.length; i ++ )  
  33.              //  运行刚才建立的100个线程   
  34.             threads[i].start();  
  35.          for  ( int  i  =   0 ; i  <  threads.length; i ++ )  
  36.              //  100个线程都执行完后继续   
  37.             threads[i].join();  
  38.         System.out.println( " n= "   +  JoinThread.n);  
  39.     }  
  40. }   


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

 

分享到:
评论

相关推荐

    volatile与synchronized的区别

    volatile与synchronized的区别,锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility)

    volatile和synchronized的区别

    ### volatile与synchronized的区别 #### 一、锁的特性:互斥与可见性 在并发编程中,锁作为实现线程安全的一种手段,其核心作用在于提供两种特性:互斥和可见性。 - **互斥**:互斥是指在任何时刻,只允许一个...

    java中volatile和synchronized的区别与联系

    Java 中 volatile 和 synchronized 的区别与联系 Java 中的 volatile 和 synchronized 是两种不同的关键字,它们都是用来解决多线程编程中出现的问题,但是它们的作用机制和使用场景却不同。 volatile volatile ...

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

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

    java多线程中的volatile和synchronized用法分析

    volatile和synchronized的主要区别在于: 1. 可见性:两者都提供可见性,但volatile只保证变量本身的可见性,而synchronized则保证整个代码块的可见性。 2. 原子性:volatile不能保证复合操作(如`count++`)的原子...

    并发编程之JMM&amp;amp;synchronized&amp;amp;volatile详解.pdf

    例如,合理使用volatile关键字可以减少不必要的缓存刷新,而明智地运用synchronized可以防止数据不一致,提高程序的正确性和可靠性。因此,深入学习并发编程的关键概念以及它们在现代计算机硬件上的实现,是成为高效...

    并发编程一之synchronized和volatile.rar

    在并发编程领域,正确理解和使用同步机制至关重要,其中`synchronized`和`volatile`是Java中两个关键的并发控制工具。本资源"并发编程一之synchronized和volatile.rar"提供了多个示例代码(demo1到demo12),帮助...

    java里的volatile关键字详解

    3. 有序性:Java语言提供了volatile 和synchronized 两个关键字来保证线程之间操作的有序性,volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized是由“一个变量在同一个时刻只允许一条线程对其进行...

    synchronized ReentrantLock volatile Atomic 原理分析.docx

    《Java并发编程:synchronized、ReentrantLock、volatile与Atomic深度解析》 在Java多线程编程中,正确地管理共享资源是至关重要的。本文将深入探讨四种关键的并发控制机制:synchronized关键字、ReentrantLock(可...

    详解java并发编程(2) --Synchronized与Volatile区别

    下面我们将详细介绍Synchronized和Volatile的区别和使用。 Synchronized Synchronized是一种同步机制,它可以保证多个线程在同一时刻只能有一个线程处于方法或者同步块中。Synchronized可以修饰方法、代码块和类,...

    java同步synchronized关键字用法示例

    volatile与synchronized的区别** `synchronized`提供了数据一致性,但会阻止线程并发执行,造成阻塞。而`volatile`关键字则可以保证变量的可见性,但不会提供互斥访问。因此,`volatile`通常用于读多写少的情况,...

    volatile用法

    5. `volatile`与`synchronized`的区别: `volatile`和`synchronized`都是Java中的关键字,虽然它们都可以用于处理多线程同步问题,但作用机制不同。`volatile`确保了变量的修改对于其他线程是可见的,而`...

    Java线程:volatile关键字

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

    synchronized关键字的实质及用法

    8. ** volatile与synchronized的区别** `volatile`关键字确保了变量的可见性,但不能保证原子性。`synchronized`则可以保证原子性和可见性,但会带来更高的开销。根据实际需求选择合适的方式。 9. **使用场景** ...

    Java多线程(Synchronized+Volatile+JUC 并发工具原理+线程状态+CAS+线程池)

    Java 多线程机制提供了两种实现同步的方式:Synchronized 和 Volatile。 1. Synchronized Synchronized 是 Java 语言中的一个关键字,用于实现线程同步。它可以保证在同一个时刻,只有一个线程可以执行某个方法...

    面试官最爱的volatile关键字.docx

    volatile与synchronized的主要区别在于: 1. volatile只能修饰实例变量和类变量,而synchronized可以作用于方法和代码块,提供了更广泛的控制范围。 2. volatile仅保证数据的可见性和有序性,而不保证原子性,而...

    Java线程之线程同步synchronized和volatile详解

    Java线程之线程同步synchronized和volatile详解 Java线程同步是Java多线程编程中非常重要的一个方面,主要涉及到线程安全、可见性和有序性三个方面。Java提供了多种同步机制来解决线程安全问题,包括synchronized和...

    volatile 变量的说明

    例如,如果你有一个计数器并尝试通过多个线程同时递增,volatile并不能保证递增操作的原子性,因此仍然需要使用synchronized或者其他并发控制机制来保证。 下面是一些关于volatile的常见问题及其解答: 1. **参数...

    [JAVA][synchronized的使用]

    - **volatile**:相比`synchronized`,`volatile`关键字轻量级且快速,但只适用于变量的读写操作,无法保证复合操作的原子性。 - **ReentrantLock**:是Java并发包`java.util.concurrent.locks`中的可重入锁,提供...

    java synchronized demo

    6. **synchronized与 volatile 的区别**: - `volatile`主要保证变量的可见性和有序性,但无法保证原子性,而`synchronized`则能保证原子性、可见性和有序性。 - `volatile`适用于读多写少的情况,而`synchronized...

Global site tag (gtag.js) - Google Analytics