(转)Java面试——线程同步volatile与synchronized详解

0. 前言

面试时很可能遇到这样一个问题:使用volatile修饰int型变量i,多个线程同时进行i++操作,这样可以实现线程安全吗?提到线程安全、线程同步,我们经常会想到两个关键字:volatile和synchronized,那么这两者有什么区别呢?

1. volatile与synchronized介绍

volatile是变量修饰符,其修饰的变量具有可见性(可见性也就是说一旦某个线程修改了该被volatile修饰的变量,它会保证修改的值会立即被更新到物理内存,当有其他线程需 要读取时,可以立即获取修改之后的值)。在Java中为了加快程序的运行效率,对一些变量的操作通常是在寄存器或是CPU缓存上进行的,之后才会同步到物理内存中,而加了volatile修饰符的变量则是直接读写物理内存。

例子请查看下面的3.1,帮助理解。

volatile可以禁止进行指令重排,什么是指令重排序?一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。指令重排序不会影响单个线程的执行,但是会影响到线程并发执行的正确性。

但是volatile可以保证有序性。程序执行到volatile变量的读操作或者写操作时,在其前面的语句中,更改操作肯定已经完成,且结果已经对后面的操作可见,在其后面的操作肯定还没有进行。

例子请查看下面3.2,帮助理解。

synchronized则作用于一段代码或方法,使用了该修饰符既可以保证可见性(通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到物理内存中。因此可以保证可见性),也能够保证原子性(原子性表现在要么不执行,要么执行到底)。有时候必须使用synchronized,而不能使用volatile。

例子请查看下面3.3,帮助理解。

2. 总结

(1)从而我们可以看出volatile虽然具有可见性但是并不能保证原子性。

(2)synchronized关键字是防止多个线程同时执行一段代码,那么就会影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized,但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性。

3. volatile与synchronized的使用场景举例(结合第1部分进行理解学习)

3.1 volatile的使用举例

class MyThread extends Thread {
    private volatile boolean isStop = false;
    public void run() {
        while (!isStop) {
            System.out.println("do something");
        }
    }
    public void setStop() {
        isStop = true;
    }
}  

线程执行run()的时候我们需要在线程中不停的做一些事情,比如while循环,那么这时候该如何停止线程呢?如果线程做的事情不是耗时的,那么只需要使用一个标志即可。如果需要退出时,调用setStop()即可。这里就使用了关键字volatile,这个关键字的目的是如果修改了isStop的值,那么在while循环中可以立即读取到修改后的值。

如果线程做的事情是耗时的,那么可以使用interrupt方法终止线程。

3.2 volatile的使用举例

//线程1:
context = loadContext();   //语句1  context初始化操作
inited = true;            //语句2
//线程2:
while(!inited ){
  sleep()
}
doSomethingwithconfig(context);  

因为指令重排序,有可能语句2会在语句1之前执行,可能导致context还没被初始化,而线程2中就使用未初始化的context去进行操作,导致程序出错。

这里如果用volatile关键字对inited变量进行修饰,就不会出现这种问题了,因为当执行到语句2时,必定能保证context已经初始化完毕。

3.3 必须使用synchronized而不能使用volatile的场景

public class Test {
    public volatile int inc = 0;
    public void increase() {
        inc++;
    }  

    public static void main(String[] args) {
        final Test test = new Test();
        for(int i=0;i<10;i++){
            new Thread(){
                public void run() {
                    for(int j=0;j<1000;j++)
                        test.increase();
                };
            }.start();
        }
        while(Thread.activeCount()>1)  //保证前面的线程都执行完
            Thread.yield();
        System.out.println(test.inc);
    }
}  

例子中用new了10个线程,分别去调用1000次increase()方法,每次运行结果都不一致,都是一个小于10000的数字。自增操作不是原子操作,volatile 是不能保证原子性的。回到文章一开始的例子,使用volatile修饰int型变量i,多个线程同时进行i++操作。比如有两个线程A和B对volatile修饰的i进行i++操作,i的初始值是0,A线程执行i++时刚读取了i的值0,就切换到B线程了,B线程(从内存中)读取i的值也为0,然后就切换到A线程继续执行i++操作,完成后i就为1了,接着切换到B线程,因为之前已经读取过了,所以继续执行i++操作,最后的结果i就为1了。同理可以解释为什么每次运行结果都是小于10000的数字。

但是使用synchronized对部分代码进行如下修改,就能保证同一时刻只有一个线程获取锁然后执行同步代码。运行结果必然是10000。

public  int inc = 0;
public synchronized void increase() {
        inc++;
}  

注:本文转载地址 http://blog.csdn.net/seu_calvin/article/details/52370068

美文美图

剑指Offer——线程同步volatile与synchronized详解的更多相关文章

  1. Java并发——线程同步Volatile与Synchronized详解

    0. 前言 转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52370068 面试时很可能遇到这样一个问题:使用volatile修饰in ...

  2. 线程同步Volatile与Synchronized(一)

    volatile 一.volatile修饰的变量具有内存可见性 volatile是变量修饰符,其修饰的变量具有内存可见性. 可见性也就是说一旦某个线程修改了该被volatile修饰的变量,它会保证修改 ...

  3. 剑指Offer——中国银行面试知识储备

    剑指Offer--中国银行面试知识储备+面试内容 事件介绍 时间:2016.11.23 08:30 地点:北京市海淀区永丰路299号南门(中国银行软件中心) 事件:中国银行面试(中英文面试) 注意事项 ...

  4. 剑指 Offer 49. 丑数 + 小根堆 + 动态规划

    剑指 Offer 49. 丑数 Offer_49 题目详情 解法一:小根堆+哈希表/HashSet 根据丑数的定义,如果a是丑数,那么a2, a3以及a*5都是丑数 可以使用小根堆存储按照从小到大排序 ...

  5. 剑指Offer——知识点储备-Java基础

    剑指Offer--知识点储备-Java基础 网址来源: http://www.nowcoder.com/discuss/5949?type=0&order=0&pos=4&pa ...

  6. 剑指Offer——完美+今日头条笔试题+知识点总结

    剑指Offer--完美+今日头条笔试题+知识点总结 情景回顾 时间:2016.9.28 16:00-18:00 19:00-21:00 地点:山东省网络环境智能计算技术重点实验室 事件:完美世界笔试 ...

  7. 剑指Offer——小米+小红书笔试题+知识点总结

    剑指Offer--小米+小红书笔试题+知识点总结 情景回顾 时间:2016.9.23 19:00-21:00 2016.9.24 15:00-17:00 地点:山东省网络环境智能计算技术重点实验室 事 ...

  8. 《剑指offer》内容总结

    (1)剑指Offer——Trie树(字典树) Trie树 Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种.典型应用是统计和排序大量的字符串(但不仅限于字符串),所以经常 ...

  9. 剑指Offer面试题:1.实现Singleton模式

    说来惭愧,自己在毕业之前就该好好看看<剑指Offer>这本书的,但是各种原因就是没看,也因此错过了很多机会,后悔莫及.但是后悔是没用的,现在趁还有余力,把这本书好好看一遍,并通过C#通通实 ...

随机推荐

  1. [JSOI2007]字符加密

    题目描述 喜欢钻研问题的JS 同学,最近又迷上了对加密方法的思考.一天,他突然想出了一种他认为是终极的加密办法:把需要加密的信息排成一圈,显然,它们有很多种不同的读法. 例如‘JSOI07’,可以读作 ...

  2. 模板 AC自动机

    题目描述 有$N$ 个由小写字母组成的模式串以及一个文本串$T$ .每个模式串可能会在文本串中出现多次.你需要找出哪些模式串在文本串$T$ 中出现的次数最多. 输入输出格式 输入格式: 输入含多组数据 ...

  3. StopAllSounds

    10月3日,在杭州市西湖景区,一只小松鼠不停地接受一道道食物,花生.玉米.饼干,可谓来者不拒,憨态可掬的模样吸引了众多围观者...Description   小松鼠开心地在树之间跳跃着,突然她停了下来 ...

  4. UVA12186

    给出一个树状关系图,公司里只有一个老板编号为0,其他人员从1开始编号.除了老板,每个人都有一个直接上司,没有下属的员工成为工人. 工人们想写一份加工资的请愿书,只有当不少于员工的所有下属的T%人递交请 ...

  5. bzoj 4518: [Sdoi2016]征途

    Description Pine开始了从S地到T地的征途. 从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站. Pine计划用m天到达T地.除第m天外,每一天晚上Pine都必须在休息站过夜 ...

  6. hdu 5641 BestCoder Round #75

    King's Phone  Accepts: 310  Submissions: 2980  Time Limit: 2000/1000 MS (Java/Others)  Memory Limit: ...

  7. 例 7-10 uva12212(迭代加深搜索)

    题意:对于一段数字,每次可以剪切一段连续的自然数,粘贴到任意部分,使其变成升序 思路: 考虑的是进行搜索,深搜并不能保证是最短,广搜感觉每层的情况太多. 迭代加深:枚举搜索深度,然后进行深搜. 这种方 ...

  8. bzoj1531[POI2005]Bank notes 单调队列优化dp

    1531: [POI2005]Bank notes Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 559  Solved: 310[Submit][Sta ...

  9. MFC 程序入口和执行流程

    MFC(微软基础类库)以C++类的形式封装了Windows API,给开发者提供了便利,但是初学者常常会疑惑MFC程序的入口在哪里?下面给大家简单介绍一下MFC 程序入口和执行流程. 一 MFC程序执 ...

  10. 110个oracle常用函数总结

    . ASCII 返回与指定的字符对应的十进制数; SQL) zero,ascii( ) space from dual; A A ZERO SPACE --------- --------- ---- ...