关闭

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

13559人阅读 评论(1) 收藏 举报
分类:

0. 前言

转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52370068

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

 

1. volatile修饰的变量具有可见性

volatile是变量修饰符,其修饰的变量具有可见性。

可见性也就是说一旦某个线程修改了该被volatile修饰的变量,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,可以立即获取修改之后的值。

Java中为了加快程序的运行效率,对一些变量的操作通常是在该线程的寄存器或是CPU缓存上进行的,之后才会同步到主存中,而加了volatile修饰符的变量则是直接读写主存

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


2. volatile禁止指令重排 

volatile可以禁止进行指令重排

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

程序执行到volatile修饰变量的读操作或者写操作时,在其前面的操作肯定已经完成,且结果已经对后面的操作可见,在其后面的操作肯定还没有进行

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


3.  synchronized 

synchronized可作用于一段代码或方法,既可以保证可见性,又能够保证原子性。

可见性体现在:通过synchronized或者Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存中。

原子性表现在:要么不执行,要么执行到底。

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

 

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

2)性能方面,synchronized关键字是防止多个线程同时执行一段代码,就会影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized。

但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性。

 

3. volatilesynchronized的使用场景举例(结合第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方法终止线程 。如果在子线程“睡觉”时被interrupt,那么子线程可以catch到InterruptExpection异常,处理异常后继续往下执行。

 

3.2 volatile的使用举例

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

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

这里如果volatile关键字对inited变量进行修饰,就不会出现这种问题了

 

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);
    }
}

例子中用new10个线程,分别去调用1000increase()方法,每次运行结果都不一致,都是一个小于10000的数字。自增操作不是原子操作,volatile 是不能保证原子性的。回到文章一开始的例子,使用volatile修饰int型变量i,多个线程同时进行i++操作。比如有两个线程ABvolatile修饰的i进行i++操作,i的初始值是0A线程执行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://www.cnblogs.com/dolphin0520/p/3920373.html以及warmor的博客


8
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

Java 多线程之 synchronized 和 volatile 的比较

在做多线程并发处理时,经常需要对资源进行可见性访问和互斥同步操作。有时候,我们可能从前辈那里得知我们需要对资源进行 volatile 或是 synchronized 关键字修饰处理。可是,我们却不知道...
  • u013761665
  • u013761665
  • 2016-04-05 09:50
  • 3876

可能忽略的Java基础知识-小心使用synchronized和volatile

基本概念1.并发编程需要理解几个特性: (1)原子性:不会被打断或破坏的一个或一组操作,原子操作是不能被线程调度机制中断的操作 (2)可见性:写操作对所有的读操作都是立即可见的,例如一个线程中对变...
  • w7849516230
  • w7849516230
  • 2016-05-14 16:52
  • 2067

volatile和synchronized的区别

volatile和synchronized特点首先需要理解线程安全的两个方面:执行控制和内存可见。执行控制的目的是控制代码执行(顺序)及是否可以并发执行。内存可见控制的是线程执行结果在内存中对其它线程...
  • suifeng3051
  • suifeng3051
  • 2016-09-21 18:16
  • 5989

java中volatile、synchronized和lock解析

1、概述在研究并发程序时,我们需要了解java中关键字volatile和synchronized关键字的使用以及lock类的用法。首先,了解下java的内存模型:(1)每个线程都有自己的本地内存空间(...
  • zhoutaochun
  • zhoutaochun
  • 2017-03-07 21:10
  • 681

synchronized和volatile的区别

volatile与synchronized介绍 volatile是变量修饰符,其修饰的变量具有可见性(可见性也就是说一旦某个线程修改了该被volatile修饰的变量,它会保证修改的值会立即被更新到物...
  • qiuzhilu
  • qiuzhilu
  • 2016-10-26 23:35
  • 284

volatile和synchronized的区别与联系

这个可能是最好的对比volatile和synchronized作用的文章了。volatile是一个变量修饰符,而synchronized是一个方法或块的修饰符。所以我们使用这两种关键字来指定三种简单的...
  • vtopqx
  • vtopqx
  • 2013-04-10 12:15
  • 5444

java之用volatile和不用volatile的区别

在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值...
  • feier7501
  • feier7501
  • 2014-02-26 21:41
  • 50540

volatile

volatile的介绍 volatile的主要作用是:提示编译器该对象的值有可能在编译器未监测的情况下被改变。    volatile类似于大家所熟知的const也是一个类型修饰符。volati...
  • jingxuewang110
  • jingxuewang110
  • 2011-09-08 10:49
  • 18853

深入理解volatile关键字

volatile这个关键字可能很多朋友都听说过,或许也都用过。在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果。在Java 5之后,volatile关键字才得以...
  • Victor_Cindy1
  • Victor_Cindy1
  • 2015-03-16 19:12
  • 7024

C语言中volatile关键字的作用

一.前言1.编译器优化介绍:由于内存访问速度远不及CPU处理速度,为提高机器整体性能,在硬件上引入硬件高速缓存Cache,加速对内存的访问。另外在现代CPU中指令的执行并不一定严格按照顺序执行,没有相...
  • tigerjb
  • tigerjb
  • 2012-04-05 10:04
  • 102943
    个人资料
    • 访问:1316467次
    • 积分:12382
    • 等级:
    • 排名:第1348名
    • 原创:139篇
    • 转载:28篇
    • 译文:2篇
    • 评论:527条
    个人说明