关闭

剑指Offer——线程同步volatile与synchronized详解

标签: java线程面试多线程volatile
42981人阅读 评论(0) 收藏 举报
分类:

 (转)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

美文美图

 



3
0
查看评论

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

Java面试——线程同步volatile与synchronized详解 0. 前言面试时很可能遇到这样一个问题:使用volatile修饰int型变量i,多个线程同时进行i++操作,这样可以实现线程安全吗?提到线程安全、线程同步,我们经常会想到两个关键字:volatile和synchroni...
  • SEU_Calvin
  • SEU_Calvin
  • 2016-08-30 19:09
  • 14203

线程同步synchronized和volatile用法及区别

要说明线程同步问题首先要说明Java线程的两个特性,可见性和有序性。多个线程之间是不能直接传递数据交互的,它们之间的交互只能通过共享变量来实现。拿上篇博文(http://blog.csdn.net/ghsau/article/details/7421217)中的例子来说明,在多个线程之间共享了Cou...
  • wlchn
  • wlchn
  • 2015-08-10 16:29
  • 727

java中volatile、synchronized和lock解析

1、概述在研究并发程序时,我们需要了解java中关键字volatile和synchronized关键字的使用以及lock类的用法。首先,了解下java的内存模型:(1)每个线程都有自己的本地内存空间(java栈中的帧)。线程执行时,先把变量从内存读到线程自己的本地内存空间,然后对变量进行操作。 (...
  • zhoutaochun
  • zhoutaochun
  • 2017-03-07 21:10
  • 812

Java 多线程之 synchronized 和 volatile 的比较

在做多线程并发处理时,经常需要对资源进行可见性访问和互斥同步操作。有时候,我们可能从前辈那里得知我们需要对资源进行 volatile 或是 synchronized 关键字修饰处理。可是,我们却不知道这两者之间的区别,我们无法分辨在什么时候应该使用哪一个关键字。本文就针对这个问题,展开讨论。
  • u013761665
  • u013761665
  • 2016-04-05 09:50
  • 3983

内存可见性和原子性:Synchronized和Volatile的比较

Java多线程之内存可见性和原子性:Synchronized和Volatile的比较     【尊重原创,转载请注明出处】http://blog.csdn.net/guyuealian/article/details/52015707      ...
  • guyuealian
  • guyuealian
  • 2016-09-13 14:58
  • 4752

如何使用 volatile, synchronized, final 进行线程间通信

原文地址:https://segmentfault.com/a/1190000004487149。感谢作者的无私分享。 你是否真正理解并会用volatile, synchronized, final进行线程间通信呢,如果你不能回答下面的几个问题,那就说明你并没有真正的理解: 对v...
  • u011794238
  • u011794238
  • 2016-02-26 11:19
  • 891

并发编程(Synchronized/volatile/atomic)

线程安全  当多个线程访问某个类是,这个类始终能表现出正常的行为,那么这个类(对象或方法)就是线程安全的。synchronized可以在任意个对象及方法上加锁,而加锁的这段代码称为互斥区或临界区。  关键字synchronized取得的锁都是对象锁,而不是把一段代码当作锁,所以示例代码中的那个线程先...
  • zxiang248
  • zxiang248
  • 2017-04-05 19:57
  • 298

java并发编程synchronized、volatile关键字及ReentrantLock类总结

1、java为解决并发问题引入的关键字synchronized, volatile ,怎么用? synchronized 修饰的方法 或者 代码块(保证可见性和排他性); synchronized修饰静态方法时(或方法时)同步的是这个对象类级别的;synchronized修饰方法时,同步的是对象实例...
  • abasen
  • abasen
  • 2016-03-25 17:36
  • 1004

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

基本概念1.并发编程需要理解几个特性: (1)原子性:不会被打断或破坏的一个或一组操作,原子操作是不能被线程调度机制中断的操作 (2)可见性:写操作对所有的读操作都是立即可见的,例如一个线程中对变量的修改,其他线程读取到的是该变量最新修改后的值 (3)有序性:源代码的顺序与执行顺序的一致性2....
  • w7849516230
  • w7849516230
  • 2016-05-14 16:52
  • 2162

volatile、ThreadLocal、synchronized等3个关键字区别

1.volatile volatile主要是用来在多线程中同步变量。 在一般情况下,为了提升性能,每个线程在运行时都会将主内存中的变量保存一份在自己的内存中作为变量副本,但是这样就很容易出现多个线程中保存的副本变量不一致,或与主内存的中的变量值不一致的情况。 而当一个变量被volatile修饰后...
  • paincupid
  • paincupid
  • 2015-08-07 22:51
  • 2778
    个人资料
    • 访问:23682517次
    • 积分:58042
    • 等级:
    • 排名:第55名
    • 原创:473篇
    • 转载:41篇
    • 译文:2篇
    • 评论:417条
    流量统计
    你问我答
     有问题?联系我。

    博客专栏
    汪国真--《热爱生命》
          热 爱 生 命
           汪国真

        我不去想是否能够成功

        既然选择了远方

        便只顾风雨兼程

        我不去想能否赢得爱情

        既然钟情于玫瑰

        就勇敢地吐露真诚

        我不去想身后会不会袭来寒风冷雨

        既然目标是地平线

        留给世界的只能是背影

        我不去想未来是平坦还是泥泞

        只要热爱生命

        一切都在意料之中
    最新评论
    轻松一下