• 面试问题


    -----2019-12-2

    1、Volatile关键字的粗浅理解
    在学习并发编程的时候了解到,volatile关键字有两个作用:

    1. 并发环境可见性:volatile修饰后的变量能够保证该变量在线程间的可见性,线程进行数据的读写操作时将绕开工作内存(CPU缓存)而直接跟主内存进行数据交互,即线程进行读操作时直接从主内存中读取,写操作时直接将修改后端变量刷新到主内存中,这样就能保证其他线程访问到的数据是最新数据

    2. 并发环境有序性:通过对volatile变量采取内存屏障(Memory barrier)的方式来防止编译重排序和CPU指令重排序,具体方式是通过在操作volatile变量的指令前后加入内存屏障,来实现happens-before关系,保证在多线程环境下的数据交互不会出现紊乱

    Volatile无法保证原子性
    由于volatile关键字的可见性,导致容易被误解其作用,以下描述是不准确的:“volatile变量对所有线程是立即可见的,对volatile变量所有的写操作都能立即反馈到其他线程中,volatile变量在各个线程中都是一致的,所以基于volatile变量的运算在并发下安全的”

    使用volatile关键字虽然能够使线程共享的变量在并发情况下完全可见,起到线程信息交互和通信的作用,但对于非原子操作,volatile并不能保证该操作的原子性(即操作过程被其他线程干扰导致信息错误和信息丢失),最简单的例子就是i++这样的自增操作,以下是一个并发情况下的自增例子:

    private static volatile int count = 0;

    public static void main(String[] args){
    Thread[] threads = new Thread[5];
    for(int i = 0; i<5; i++){
    threads[i] = new Thread(()->{
    try{
    for(int j = 0; j<10; j++){
    System.out.println(++count);
    Thread.sleep(500);
    }
    }catch (Exception e){
    e.printStackTrace();
    }
    });
    threads[i].start();
    }
    }
    生成5条线程,每条线程都对count执行10次自增操作,按理说最后的结果应该是1到50均被打印出来,但不管运行多少次,都无法得到期望的结果,说明volatile标记的变量在并发环境下并不能保证线程安全。

    诸如“i++”或者“++i”这样的操作并不是原子操作,因为自增操作包括三个基本指令:读取数据、计算数据、输出结果,可以看看i++相关的字节码:

    Code:
    0: getstatic #2 // Field count:I
    3: iconst_1
    4: iadd
    5: putstatic #2 // Field count:I
    8: return
    getstatic指令将变量从主内存中读取出来,这时候如果该变量时volatile修饰的,那可以完全保证此时取到的是最新信息,但在iconst_1指令(入栈)和iadd指令(自增计算)执行过程中,该变量有可能正在被其他线程修改,最后计算出来的结果照样存在问题,因此volatile并不能保证非原子操作的原子性,仅在单次读或者单次写这样的原子操作中,volatile能够实现线程安全

    volatile如何保持内存可见性

    volatile的特殊规则就是:

    • read、load、use动作必须连续出现
    • assign、store、write动作必须连续出现

    所以,使用volatile变量能够保证:

    • 每次读取前必须先从主内存刷新最新的值。
    • 每次写入后必须立即同步回主内存当中。

    Volatile结合CAS实现原子性
    2、CAS(CompareAndSwap)

    比较交换原则结合volatile,就能够实现基本的线程安全,典型的应用就是concurrent包下的Atomic类,上述例子如果用AtomicInteger代替int,就能够实现自增情况下的线程安全

    3、堆排序?

  • 相关阅读:
    拓扑排序
    Codeforces #503 C. Elections(贪心,逆向
    Codeforces #367 (Div. 2) D. Vasiliy's Multiset (trie 树)
    字典树
    最大子段和
    P1880 [NOI1995] 石子合并
    P1140 相似基因
    P1280 尼克的任务
    [BZOJ4064/Cerc2012]The Dragon and the knights
    [BZOJ4066]简单题
  • 原文地址:https://www.cnblogs.com/erfsfj-dbc/p/11973864.html
Copyright © 2020-2023  润新知