Java多线程 volatile适用的场景: 纯赋值的操作

文章目录

      • volatile 修饰布尔的标记位
      • volatile 修饰布尔适用的场景
      • volatile 修饰布尔的标记位不适用的场景

volatile 修饰布尔的标记位

volatile 适用于 boolean flag 如果一个共享变量, 一直只被各个线程赋值, 而没有其他的操作, 那么就可以用volatile来代替synchronized 或者代替原子变量, 因为对布尔变量的赋值自身是有原子性的, 而volatile保证了可见性, 就满足了线程安全.
因为布尔值, 只是改成true或者false, 具有原子性, 而不是像 a++操作是非原子性的.

volatile 修饰布尔适用的场景

如下的代码演示了 volatile 适用的场景. 关键是如下的方法setDone,只是直接把done 改成了true, 而没有像a++操作那样, 需要读取a之前的值 . 只是修改布尔值是原子性的. 因此适用.

Java多线程 volatile适用的场景: 纯赋值的操作_第1张图片

package com.thread.jmm;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 类名称:UseVolatileInBoolean
 * 类描述:volatile 适用于 Boolean变量的并发修改 赋值
 *
 * @author: https://javaweixin6.blog.csdn.net/
 * 创建时间:2020/9/6 10:41
 * Version 1.0
 */
public class UseVolatileInBoolean  implements Runnable {
    volatile boolean done  = false;

    //原子类, 统计执行了多少次
    AtomicInteger atomicInteger = new AtomicInteger();

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

        thread1.start();
        thread2.start();

        //主线程等待子线程执行完毕
        thread1.join();
        thread2.join();

        System.out.println(runnable.done);
        System.out.println("运行次数 : "+runnable.atomicInteger.get());
    }

    @Override
    public void run() {
            for (int i = 0; i < 10000; i++) {
                setDone();
                atomicInteger.incrementAndGet();
            }
    }

    /**
     * 关键是此处的 setDone 与之前的done这个布尔变量的值的情况是无关的.
     * 此处只管把done改成true即可, 而不需要考虑这个布尔变量之前的情况
     *
     */
    private void setDone() {
        done = true;;
    }
}

如下图所示的运行结果是为true
Java多线程 volatile适用的场景: 纯赋值的操作_第2张图片

volatile 修饰布尔的标记位不适用的场景

如下的代码演示了volatile 修饰布尔的标记位不适用的场景 . 主要是flipDone方法中, 执行的是done = !done 是要先读取done的值, 再修改, 那么此时就不再是原子性的操作, 就可能会出现并发安全性问题.

package com.thread.jmm;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 类名称:UseVolatileInBoolean
 * 类描述:volatile 不适用的情况 Boolean
 *
 * @author: https://javaweixin6.blog.csdn.net/
 * 创建时间:2020/9/6 10:41
 * Version 1.0
 */
public class IncorrentVolatileInBoolean implements Runnable {
    volatile boolean done  = false;

    //原子类, 统计执行了多少次
    AtomicInteger atomicInteger = new AtomicInteger();

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

        thread1.start();
        thread2.start();

        //主线程等待子线程执行完毕
        thread1.join();
        thread2.join();

        System.out.println(runnable.done);
        System.out.println("运行次数 : "+runnable.atomicInteger.get());
    }

    @Override
    public void run() {
            for (int i = 0; i < 10000; i++) {
                flipDone();
                atomicInteger.incrementAndGet();
            }
    }

    /**
     * 改成相反的情况 .
     * 此时就需要考虑到done之前的情况了
     */
    private boolean flipDone() {
        done = !done;
        return done;
    }

}

多次运行 , 可以看到 , 有时是返回true, 有时是返回的false
Java多线程 volatile适用的场景: 纯赋值的操作_第3张图片

Java多线程 volatile适用的场景: 纯赋值的操作_第4张图片
总结: 无论这个变量是什么类型, 只要这个变量只是被赋值的, 而没有其他的操作, 例如对比, 或者取值等, 那么此时对于volatile是适用的. 原因有两点:
赋值是原子性的, volatile又保证了可见性.

你可能感兴趣的:(Java多线程基础与核心)