java线程同步volatile与synchronized(二)

来源:互联网 发布:知乎健身的变化女生 编辑:程序博客网 时间:2024/05/17 22:03

前段时间面试时遇到这样一个问题:使用volatile修饰int型变量i,多个线程同时进行i++操作,这样可以实现线程安全吗?

我感觉是不可以的,但是又说不出来为什么。下来后翻看了许多资料,终于了解了volatile的含义和用法了,一起来看看吧。


提到线程同步,我们经常会想到两个关键字:volatilesynchronized,那么这两者有什么区别呢?
volatile是变量修饰符,其修饰的变量具有可见性。在Java中为了加快程序的运行效率,对一些变量的操作通常是在寄存器或是cpu缓存上进行的,之后才会同步到内存中,而加了volatile修饰符的变量则是直接读写内存。可见性也就说一旦某个线程修改了该变量,其他线程读值时可以立即获取修改之后的值。

synchronized则作用于一段代码或方法,使用了该修饰符既可以保证可见性,也能够保证原子性。可见性表现在虽然其修饰的代码段或方法里面的读写操作可能在cpu缓存上进行的,不过在出代码段或方法前会把缓存中的数据同步到内存中。原子性表现在要么不执行,要么执行到底。


原子性看起来简单,其实不然,不信?看看下面几个例子:
x = 10;        //语句1
y = x;         //语句2
x++;           //语句3
x = x + 1;     //语句4
这四个语句哪些是原子操作?其实只有语句1是原子操作,怎么样,想不到吧?^^下面我来解释下
语句1就是1个动作,把10写入到内存。
语句2两个动作,先读取x的值,然后再写入内存。
语句3,4是一样的,有三个动作,读取x的值,计算,写入内存。
知道了这些,那么开头的问题就好理解了,比如有两个线程A和B对volatile修饰的i进行++操作,i的初始值是0,A线程执行i++时刚读取了i的值0,就切换到B线程了,B线程(从内存中)读取i的值也为0,然后就切换到A线程继续执行i++操作,完成后i就为1了,接着切换到B线程,因为之前已经读取过了,所以继续执行i++操作,最后的结果i就为1了。
从这里可以看出volatile虽然具有可见性但是并不能保证原子性。
volatile一般是用来作为状态标志的,看个我在java中如何结束线程 一文中举的例子:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. class MyThread extends Thread {           
  2.     private volatile boolean isStop = false;        
  3.     public void run() {    
  4.         while (!isStop) {    
  5.             System.out.println("do something");    
  6.         }    
  7.     }    
  8.     public void setStop() {    
  9.         isStop = true;    
  10.     }          
  11. }  
使用synchronized主要是用来保证线程安全的,看一个经典的单例模式(双重校验锁):
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. class Singleton{  
  2.     private volatile static Singleton instance = null;       
  3.     private Singleton() {}       
  4.     public static Singleton getInstance() {  
  5.         if(instance==null) {  
  6.             synchronized (Singleton.class) {  
  7.                 if(instance==null)  
  8.                     instance = new Singleton();  
  9.             }  
  10.         }  
  11.         return instance;  
  12.     }  
  13. }  
synchronized的另一种用法是这样的(懒汉模式):
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. class Singleton{  
  2.     private volatile static Singleton instance = null;       
  3.     private Singleton() {}       
  4.     public static synchronized Singleton getInstance() {          
  5.         if(instance==null)  
  6.             instance = new Singleton();              
  7.         return instance;  
  8.     }  
  9. }  
(哇塞volatile跟synchronized同台了耶!)
最后再来两篇扩展,写的都很棒!
1、volatile关键字解析,从内存模型,并发中的关键概念,讲到volatile的使用场景点击打开链接
2、多线程同步的五种方法点击打开链接
0 0
原创粉丝点击