您的位置:首页 > 编程语言 > Java开发

java线程同步volatile与synchronized(二)

2017-02-15 15:04 399 查看
前段时间面试时遇到这样一个问题:使用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

 





class MyThread extends Thread {           

    private volatile boolean isStop = false;        

    public void run() {    

        while (!isStop) {    

            System.out.println("do something");    

        }    

    }    

    public void setStop() {    

        isStop = true;    

    }          

}  

使用synchronized主要是用来保证线程安全的,看一个经典的单例模式(双重校验锁):

[java] view
plain copy

 





class Singleton{  

    private volatile static Singleton instance = null;       

    private Singleton() {}       

    public static Singleton getInstance() {  

        if(instance==null) {  

            synchronized (Singleton.class) {  

                if(instance==null)  

                    instance = new Singleton();  

            }  

        }  

        return instance;  

    }  

}  

synchronized的另一种用法是这样的(懒汉模式):

[java] view
plain copy

 





class Singleton{  

    private volatile static Singleton instance = null;       

    private Singleton() {}       

    public static synchronized Singleton getInstance() {          

        if(instance==null)  

            instance = new Singleton();              

        return instance;  

    }  

}  

(哇塞volatile跟synchronized同台了耶!)
最后再来两篇扩展,写的都很棒!
1、volatile关键字解析,从内存模型,并发中的关键概念,讲到volatile的使用场景点击打开链接
2、多线程同步的五种方法点击打开链接
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: