【Java 并发编程】线程简介 ( 原子操作 | volatile 关键字使用场景 )

举报
韩曙亮 发表于 2022/01/13 01:11:42 2022/01/13
【摘要】 文章目录 一、原子操作二、volatile 关键字使用场景 一、原子操作 原子操作 : read : 从 主内存 中的线程共享变量中读取数据 ; load : 将...





一、原子操作



原子操作 :

read :主内存 中的线程共享变量中读取数据 ;
load : 将从主内存读取到的数据 , 加载到 线程工作内存 中 ;

read 和 load 操作一定是 成对出现 的 , 只要从主内存中读取到数据 , 一定会将这个数据加载到线程的工作内存中 ;

use : 从线程共享变量副本读取到线程的 执行引擎 中 ;
assign : 从执行引擎中写出数据到变量的 共享变量副本 中 ;

store : 将数据从线程工作内存传输到 主内存 中 ;
write : 将数据赋值给主内容中的线程 共享变量 ;

lock : 作用于 主内存中的线程共享变量 , 将该变量标识为 被某个线程独自占用状态 ; 表示该变量只有一个线程可以进行访问 ;
unlock : 解锁 主内存中的共享变量 , 其它线程可以进行访问 ;





二、volatile 关键字使用场景



在下面的示例中 , 设置一个标志位 , 主线程开始后 , 启动一个线程 , 休眠 1000 1000 1000 毫秒 , 然后修改该标志位 , 主线程中根据标志位进行循环 , 如果标志位被修改 , 则循环停止 , 但是循环一直没有停止 ;

也就是说线程中修改的值 , 仅修改了该线程中工作内存中的标志位副本的值 ;

主内存中的值没有被修改 ;


代码示例 :

public class Main {

    private static boolean flag = false;

    private static void changeFlag() {
        System.out.println("修改标志位开始");
        flag = true;
        System.out.println("修改标志位结束");
    }

    public static void main(String[] args) {
        // 在该线程中 , 1 秒后修改标志位为 false
        new Thread(){
            @Override
            public void run() {
                super.run();
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                changeFlag();
            }
        }.start();

        // 此处如果 flag 一直为 flase 就会进入死循环
        //      如果 flag 为 true 则程序结束
        while (!flag) {

        }

        System.out.println("主线程结束");
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

执行结果 :

在这里插入图片描述


原理分析 :

线程的工作内存中 , 将 flag 修改为 true , 这只是在 CPU 缓存 中修改的 , 没有在主内存中修改这个共享变量值 , 因此主线程访问该值 , 还是 false ;

使用 volatile 关键字 , 禁用 CPU 的缓存 , 直接在主内存中进行读写 , 这样就可以解决多个线程中 共享变量 不同步的问题 ;

注意 : 只能是 线程共享变量 使用该关键字 , 设置该关键字会影响线程的执行效率 , 效率会降低 ;


使用了 volatile 关键字后的效果 :

public class Main {

    private static volatile boolean flag = false;

    private static void changeFlag() {
        System.out.println("修改标志位开始");
        flag = true;
        System.out.println("修改标志位结束");
    }

    public static void main(String[] args) {
        // 在该线程中 , 1 秒后修改标志位为 false
        new Thread(){
            @Override
            public void run() {
                super.run();
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                changeFlag();
            }
        }.start();

        // 此处如果 flag 一直为 flase 就会进入死循环
        //      如果 flag 为 true 则程序结束
        while (!flag) {

        }

        System.out.println("主线程结束");
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

执行结果 :

在这里插入图片描述


Java 并发的 3 3 3 特性 :

  • 原子性 : 每个操作都是 不可拆分的原子操作 ; 在线程中进行 a++ 就不是原子操作 , 该操作分为 3 3 3 个步骤 , 首先从主内存中读取 a 变量 , 然后进行自增操作 , 最后在将自增后的值写回主内存中 ;
  • 可见性 : 多个线程 访问同一个变量 , 该变量一旦被 某个线程修改 , 这些线程必须可以 立刻看到被修改的值 ;
  • 有序性 : 程序按照 代码先后顺序 执行 ;

volatile 关键字 , 禁用了 CPU 缓存 , 解决的是共享变量可见性问题 ;

文章来源: hanshuliang.blog.csdn.net,作者:韩曙亮,版权归原作者所有,如需转载,请联系作者。

原文链接:hanshuliang.blog.csdn.net/article/details/120169464

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。