首页 > 其他分享 >JUC系列之(二)volatile关键字

JUC系列之(二)volatile关键字

时间:2024-02-29 19:35:23浏览次数:19  
标签:JUC threadDemo 关键字 flag 线程 volatile 共享 public

volatile关键字-内存可见性

引出内存可见性问题的示例:

package com.atguigu.juc;

public class TestVolatile {
    public static void main(String[] args) {
        // 线程threadDemo修改共享变量的值
        ThreadDemo threadDemo = new ThreadDemo();
        new Thread(threadDemo).start();

        // main线程读取共享变量的值
        while (true){
            if (threadDemo.isFlag()){
                System.out.println("------------------");
                break;
            }
        }
    }
}

class ThreadDemo implements Runnable {
    private boolean flag = false;

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {

        }
        flag = true;

        System.out.println("flag=" + isFlag());
    }
}

一直没有执行结束,像是main线程读取的共享变量的值一直都是false,导致main线程一直没有结束

内存可见性问题:

多个线程由于存在独立的缓存,因此在操作共享数据时彼此是不可见的(一个线程操作了共享变量之后,另一个线程看到的共享变量的状态还是改变之前的)

解决内存可见性问题:

通过同步锁可以解决内存可见性问题

package com.atguigu.juc;

public class TestVolatile {
    public static void main(String[] args) {
        // 线程threadDemo修改共享变量的值
        ThreadDemo threadDemo = new ThreadDemo();
        new Thread(threadDemo).start();

        // main线程读取共享变量的值
        while (true){
            // 同步锁能够保证main线程每次都能刷新缓存,去主存中读数据,保证每次读到的共享数据都是最新的
            synchronized (threadDemo) {
                if (threadDemo.isFlag()){
                    System.out.println("------------------");
                    break;
                }
            }
        }
    }
}

class ThreadDemo implements Runnable {
    private boolean flag = false;

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {

        }
        flag = true;

        System.out.println("flag=" + isFlag());
    }
}

volatile关键字:既想解决内存可见性问题,又不想通过加同步锁的方式解决(加锁后效率极低),因此出现了该关键字

当多个线程进行操作共享数据时,可以保证内存中的数据可见。(一个线程操作了共享变量之后,另一个线程能够马上看到共享变量状态的变化)

使用了volatile关键字,就相当于线程1是直接改的主存中的共享变量,main线程是实时从主存中读取的共享变量的值。

volatile关键字原理:内存栅栏

volatile关键字造成性能下降的原因:被volatile关键字修饰后,不能再进行重排序了

package com.atguigu.juc;

public class TestVolatile {
    public static void main(String[] args) {
        // 线程threadDemo修改共享变量的值
        ThreadDemo threadDemo = new ThreadDemo();
        new Thread(threadDemo).start();

        // main线程读取共享变量的值
        while (true){
            if (threadDemo.isFlag()){
                System.out.println("------------------");
                break;
            }
        }
    }
}

class ThreadDemo implements Runnable {
    // 使用volatile关键字修饰共享变量
    private volatile boolean flag = false;

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {

        }
        flag = true;

        System.out.println("flag=" + isFlag());
    }
}

解决了内存可见性问题就相当于各个线程在直接操作主存中的数据

volatile和synchronized的比较:

volatile仅仅是更轻量级的一种同步策略

volatile不具备“互斥性”
volatile不能保证变量的“原子性”

标签:JUC,threadDemo,关键字,flag,线程,volatile,共享,public
From: https://www.cnblogs.com/wzzzj/p/18045235

相关文章

  • C++ 关键字
    C++关键字alignas和alignof用法alignasalignas指定了内存按照多少对齐。alignas(0)这种写法无效,编译器会无视你的这个代码structalignas(8)S{};//表示是8个字节的对齐方式structalignas(1)U{Ss;};//虽然里面有个S,但是依然指定了该结构体的内存对齐要求为1字......
  • __weak关键字和__attribute__ --20240225
    __weak关键字__weak是一个c/c++编译器关键字,用于定义一个弱化符号。弱化符号是一种在链接阶段可以被覆盖的符号,允许多个同名符号存在于不同的目标文件中,而不会产生冲突。 当一个符号被声明为__weak时,它具有两个特性:1、如果该符号在某个目标文件中被定义,那么这个定义将成为默......
  • extern、const、register、static、inline关键字 --20240225
    extern关键字extern关键字有两种用法:1、用于声明一个全局变量或函数的外部链接性2、extern"C"是一个语言特性,用于告诉编译器按照C语言的方式对待指定的代码块,以确保与C语言兼容 用法一:用于声明一个全局变量或函数的外部链接性//file1.c#include<stdio.h>intn......
  • 类变量和类方法、代码块、单例设计模式、final关键字、抽象类、接口、内部类
    类变量和类方法类变量-提出问题说:有一群小孩在玩堆雪人,不时有新的小孩加入,请问如何知道现在共有多少人在玩?,编写程序解决。传统的方法来解决思路在main方法中定义一个变量count当一个小孩加入游戏后count++,最后个count就记录有多少小孩玩游戏小孩是一个类,有名字属......
  • volatile及内存屏障理解总结
    volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些未知的因素更改。volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存......
  • 九、virtual关键字
    九、virtual关键字为什么要使用虚函数:多态:虚函数允许我们通过基类指针或引用来调用派生类的实现,从而实现多态。这使得我们可以编写更通用、可扩展的代码。可扩展性:通过使用虚函数,我们可以轻松地添加新的派生类,而无需修改现有的基类代码。代码重用:虚函数允许派生类重用和扩展......
  • const与mutable关键字
    1异步操作,使用lambda表达式,参数采用传值方式;window直接修改传输参数的值,并打印使用正常;linux报错:errorpassingconst**asthisargumentof**discardsqualifier[-fpermissive]const关键字用于类的成员函数,成为常成员函数,即:不允许在常成员函数的内部(实现里)修改......
  • Java基础02:标识符和关键字
    1.标识符1.1关键字 Java所有组成的部分都需要名字。类名、变量名以及方法名都被称为标识符。1.2标识符注意点1.2.1所有的标识符都应该以字母(A-Z或者a-z),美元符($)、或者下划线(_)开始1.2.2首字符之后可以是字母(A-Z或者a-z),美元符($)、或者......
  • linux统计字符串出现次数(linux查询关键字出现的个数了解)
     使用脚本统计字符串出现次数#!/bin/bash#获取要监控的本地服务器IP地址IP=`ifconfig|grepinet|grep-vE'inet6|127.0.0.1'|awk'{print$2}'`echo"IP地址:"$IP#获取cpu总核数cpu_num=`grep-c"modelname"/proc/cpuinfo`echo"cpu总核数:&q......
  • 多线程系列(四) -volatile关键字使用详解
    一、简介在上篇文章中,我们介绍到在多线程环境下,如果编程不当,可能会出现程序运行结果混乱的问题。出现这个原因主要是,JMM中主内存和线程工作内存的数据不一致,以及多个线程执行时无序,共同导致的结果。同时也提到引入synchronized同步锁,可以保证线程同步,让多个线程依次排队执行......