volatile关键字

1.什么是volatile?

        1.1.volatile是一种同步机制,比synchronized或Lock更轻量级,因为使用volatile并不会发生线程“上下文切换”等开销很大的行为,volatile关键字只是把被修

              饰的变量修改后刷新到“主内存”中;

        1.2.如果一个变量被volatile修饰,那么JVM就知道这个变量可能会被并发修改;

        1.3.volatile属于轻量级同步机制,做不到像synchronized那样的原子保护,其仅在有限的场景下使用;

2.volatile使用场景

适用场景

        1.纯赋值

       如果一个共享变量自始至终只被各个线程赋值,而没有其它操作(运算或取反等操作),那么就可以使用volatile代替synchronized或者“原子变量”,因为赋值本身是原子性的,而volatile又保证了可见性所以是线程安全的;

import java.util.concurrent.atomic.AtomicInteger;
/**
 * 演示volatile适用于纯赋值场景用于保证线程安全
 */
public class UseVolatile implements Runnable{
    volatile boolean flag = false;
    AtomicInteger realA = new AtomicInteger();

    @Override
    public void run() {
        for (int i = 0; i <10000 ; i++) {
            setFlag();
            realA.incrementAndGet(); // 累加
        }
    }

    private void setFlag() {
        flag = true; // 此种写法无线程安全问题,纯赋值
     // flag = !flag; // 若这样写则有线程安全问题,因为此处的赋值依赖于之前的变量值;  
    }

    public static void main(String[] args) throws InterruptedException {
        UseVolatile r = new UseVolatile();
        Thread thread1 = new Thread(r);
        Thread thread2 = new Thread(r);

        thread1.start();
        thread2.start();
        // 主线程等待子线程执行完
        thread1.join();
        thread2.join();
        System.out.println("volatile适用于纯赋值: " + r.flag);
        System.out.println("执行次数: " + r.realA); // 20000
    }
}

2.作为刷新之前变量的触发器

        伪代码示例

/* 模拟volatile作为“触发器”多线程初始化完成后去执行任务 */
Map configOptions;
cahr[] configText;
volatile boolean initialized = false; // 此字段被volatile修饰,用于判断是否完成初始化

// 线程一
configOptions = new HashMap();
configText = readConfigFile(fileName);
processConfigOptions(configText,configOptions)
initialized = true; // initialized被volatile修饰具有禁止指令重排性,当initialized=true;代表上面configOptions、configText等参数或方法已经完成了初始化; 


// 线程二
while(!initialized){ // 此时的initialized作为触发器(因为initialized被volatile修饰具有可见性所以线程一的修改线程二能看见,只有当线程一完成了初始化,线程二才会去具体执行的业务,不然一直在循环中休眠等待初始化完成...)
    Thread.sleep(1000);
}

// 线程一初始化完成线程二去做其它业务..use config make todo...

不适用场景

        1.不适用于a++

import java.util.concurrent.atomic.AtomicInteger;
/**
 * 演示volatile不适用于a++场景
 */
public class NoVolatile implements Runnable{
    volatile int a;
    // 变量realA主要用于与变量a做对比
    AtomicInteger realA = new AtomicInteger();

    @Override
    public void run() {
        for (int i = 0; i <10000 ; i++) {
            a++;
            realA.incrementAndGet(); // 累加
        }
    }

    public static void main(String[] args) throws InterruptedException {
        NoVolatile r = new NoVolatile();
        Thread thread1 = new Thread(r);
        Thread thread2 = new Thread(r);

        thread1.start();
        thread2.start();
        // 主线程等待子线程执行完
        thread1.join();
        thread2.join();
        System.out.println("使用volatile修饰变量a的最终累加值为: " + r.a);
        System.out.println("最终累加正确值应为: " + r.realA); // 20000
    }
}

3.volatile的两点作用

3.1.可见性

     读一个volatile修饰的变量前需先使这个变量在线程内部相应的缓存失效(内部缓存:即每个线程独占的缓存),这样就必须去主内存中读取最新值(主内存:即所有线程共享的内存),当写一个volatile变量时会立即刷入到主内存中;

3.2.禁止指令重排序

        

4.volatile与synchronized关系

       volatile可以看做是一个轻量级的synchronized,如果一个共享变量自始至终都是被各个线程赋值,而没有做其它操作(运算或取反等操作),那么就可以使

用volatile代替synchronized,因为赋值本身是原子性的,而volatile又保证了可见性所以是线程安全的;

5.volatile总结

        5.1.volatile适用于一个共享变量自始至终只被各个线程赋值,而没有其它操作(运算或取反等操作),那么就可以使用volatile代替synchronized或者“原子变

               量”,因为赋值本身是原子性的,而volatile又保证了可见性所以是线程安全的;或者也可以使用volatile作为触发器实现轻量数据同步;

        5.2.volatile属性的读写操作都是无锁的,它不能替代synchronized因为它没有提供原子性互斥性,因为无锁所以不需要花时间在获取锁和释放锁上所以

               它是轻量级的;

        5.3.volatile只能修饰属性,使用volatile修饰的属性不会被编译器“指令重排”;

        5.4.volatile提供了可见性,任何一个线程对其修饰的属性进行修改,其它线程会立马可见,volatile属性不会被线程缓存,始终从主内存中取;

        5.5.volatile提供了happens-before保证;       

        5.6.volatile可以j使long和double的赋值是原子的(long和double本身赋值并不能保证是原子性的);

你可能感兴趣的:(多线程,java,jvm,开发语言)