您好,欢迎来到海洋目录网!网站收录,值得选择!长期招友情链接 QQ10212321

快审
当前位置:海洋目录网 » 站长资讯 » 站长资讯 » 文章详细 订阅RssFeed

volatile关键字修饰对象是什么效果?

来源:本站原创 浏览:11次 时间:2020-06-30

volatile关键字修饰对象是什么效果?

如果volatile的修饰的是一个引用类型的对象变量,那么对象中定义的一些普通全局变量是否会受到volatile关键字的效果影响呢?”

接下来,我们就一起来分析下这个问题!让我们先通过一个例子来回顾下volatile关键字的作用!

public class VolatitleFoo {    //类变量    final static int max = 5;    static int init_value = 0;    public static void main(String args[]) {        //启动一个线程,当发现local_value与init_value不同时,则输出init_value被修改的值        new Thread(() -> {            int localValue = init_value;            while (localValue < max) {                if (init_value != localValue) {                    System.out.printf("The init_value is update ot [%d]\n", init_value);                    //对localValue进行重新赋值                    localValue = init_value;                }            }        }, "Reader").start();        //启动updater线程,主要用于对init_value的修改,当local_value=5的时候退出生命周期        new Thread(() -> {            int localValue = init_value;            while (localValue < max) {                //修改init_value                System.out.printf("The init_value will be changed to [%d]\n", ++localValue);                init_value = localValue;                try {                    TimeUnit.SECONDS.sleep(2);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }, "Updater").start();    }}

在上面的代码示例中,我们定义了两个类变量max、init_value,然后在主线程中分别启动一个Reader线程,一个Updater线程。Updater线程做的事情就是在值小于max的值时以每两毫秒的速度进行自增。而Reader线程则是在感知init_value值发生变化的情况下进行读取操作。

期望的效果是线程Updater更新init_value值之后,可以立刻被线程Reader感知到,从而进行输出显示。实际运行效果如下:

The init_value will be changed to [1]The init_value will be changed to [2]The init_value will be changed to [3]The init_value will be changed to [4]The init_value will be changed to [5]

实际的运行效果是在Updater修改类变量init_value后,Reader线程并没有立马感知到变化,所以没有进行相应的显示输出。而原因就在于共享类变量init_value在被线程Updater拷贝到该线程的工作内存中后,Updater对变量init_value的修改都是在工作内存中进行的,完成操作后没有立刻同步回主内存,所以Reader线程对其改变并不可见。

为了解决线程间对类变量init_value的可见性问题,我们将类变量init_value用volatile关键字进行下修饰,如下:

static volatile int init_value = 0;

然后我们再运行下代码,看看结果:

The init_value will be changed to [1]The init_value is update ot [1]The init_value will be changed to [2]The init_value is update ot [2]The init_value will be changed to [3]The init_value is update ot [3]The init_value will be changed to [4]The init_value is update ot [4]The init_value will be changed to [5]The init_value is update ot [5]

此时线程Updater对类变量init_value的修改,立马就能被Reader线程感知到了,这就是volatile关键字的效果,可以让共享变量在线程间实现可见,原因就在于在JVM的语义层面要求被volatile修饰的共享变量,在工作内存中的修改要立刻同步回主内存,并且读取也需要每次都重新从主内存中刷新一份到工作内存中后才可以操作。

关于以上适用volatile关键字修饰基本类型的类变量、实例变量的场景,相信大家会比较好理解。接下来,我们来继续改造下代码:

public class VolatileEntity {    //使用volatile修饰共享资源i    //类变量    final static int max = 5;    int init_value = 0;    public static int getMax() {        return max;    }    public int getInit_value() {        return init_value;    }    public void setInit_value(int init_value) {        this.init_value = init_value;    }    private static class VolatileEntityHolder {        private static VolatileEntity instance = new VolatileEntity();    }    public static VolatileEntity getInstance() {        return VolatileEntityHolder.instance;    }}

我们将之前代码中的类变量init_value放到实体类VolatileEntity中,并将其设计为一个实例变量,为了便于理解,我们将实体类VolatileEntity设计为单例模式,确保两个线程操作的是同一个共享堆内存对象。如下:

public class VolatileEntityTest {    //使用volatile修饰共享资源    private static VolatileEntity volatileEntity = VolatileEntity.getInstance();    private static final CountDownLatch latch = new CountDownLatch(10);    public static void main(String args[]) throws InterruptedException {        //启动一个线程,当发现local_value与init_value不同时,则输出init_value被修改的值        new Thread(() -> {            int localValue = volatileEntity.init_value;            while (localValue < VolatileEntity.max) {                if (volatileEntity.init_value != localValue) {                    System.out.printf("The init_value is update ot [%d]\n", volatileEntity.init_value);                    //对localValue进行重新赋值                    localValue = volatileEntity.init_value;                }            }        }, "Reader").start();        //启动updater线程,主要用于对init_value的修改,当local_value=5的时候退出生命周期        new Thread(() -> {            int localValue = volatileEntity.init_value;            while (localValue < VolatileEntity.max) {                //修改init_value                System.out.printf("The init_value will be changed to [%d]\n", ++localValue);                volatileEntity.init_value = localValue;                try {                    TimeUnit.SECONDS.sleep(2);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }, "Updater").start();    }}

在上述代码中线程Updater和Reader此时操作的是类变量VolatileEntity对象中的普通实例变量init_value。在VolatileEntity对象没被volatile关键字修饰之前,我们看下运行效果:

The init_value will be changed to [1]The init_value will be changed to [2]The init_value will be changed to [3]The init_value will be changed to [4]The init_value will be changed to [5]

与未被volatile修饰的int类型的类变量效果一样,线程Updater对VolatileEntity对象中init_value变量的操作也不能立马被线程Reader可见。如果此时我们不VolatileEntity类中单独用volatile关键字修饰init_value变量,而是直接VolatileEntity对象用volatile关键字修饰,效果会如何呢?

private static volatile VolatileEntity volatileEntity = VolatileEntity.getInstance();

此时VolatileEntity对象的引用变量被volatile关键字修饰了,然而其中的普通实例变量init_value并没有直接被volatile关键字修饰,然后我们在运行下代码看看效果:

The init_value will be changed to [1]The init_value is update ot [1]The init_value will be changed to [2]The init_value is update ot [2]The init_value will be changed to [3]The init_value is update ot [3]The init_value will be changed to [4]The init_value is update ot [4]The init_value will be changed to [5]The init_value is update ot [5]

从实际的运行效果上看,虽然我们没有直接用volatile关键字修饰对象中的类变量init_value,而是修改了对象的引用,但是我们看到对象中的普通实例变量仍然实行了线程间的可见性,也就是说间接也相当于被volatile关键字修饰了。所以,在这里问题也就基本上有了答案,那就是:“被volatile关键字修饰的对象作为类变量或实例变量时,其对象中携带的类变量和实例变量也相当于被volatile关键字修饰了”。

这个问题主要是考查大家对volatile关键字的理解是否深入,另外也是对Java数据存储结构的考查,虽然可能大家对volatile关键字的作用会有了解,但是如果突然被问到这样的问题,如果不加以思考,在面试中也是很容易被问懵的!

推荐站点

  • At-lib分类目录At-lib分类目录

    At-lib网站分类目录汇集全国所有高质量网站,是中国权威的中文网站分类目录,给站长提供免费网址目录提交收录和推荐最新最全的优秀网站大全是名站导航之家

    www.at-lib.cn
  • DMOZ目录DMOZ目录

    DMOZ中文网站分类目录-免费收录各类优秀网站的中文网站目录.由人工编辑,并提供网站分类目录检索及地区分类目录检索,是站长免费推广网站的有力平台!

    www.dmozdir.cn
  • 中国链接目录中国链接目录

    中国链接目录简称链接目录,是收录优秀网站和淘宝网店的网站分类目录,为您提供优质的网址导航服务,也是网店进行收录推广,站长免费推广网站、加快百度收录、增加友情链接和网站外链的平台。

    www.cnlink.org
  • 35目录网35目录网

    35目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向35目录推荐、提交优秀网站。

    www.35mulu.com
  • Adcd网站目录Adcd网站目录

    Adcd网站目录是全免费的网址提交目录,收录国内外、各行业优秀网站,为用户提供全面的网站分类目录网站、优秀网站参考、友情链接交换平台、网站推广服务国内免费的网站提交入口(www.adcd.cn)。

    https://www.adcd.cn