您好,欢迎来到站长目录(28sn.com)!


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

来源:网络整理 浏览:333次 时间: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关键字的作用会有了解,但是如果突然被问到这样的问题,如果不加以思考,在面试中也是很容易被问懵的!

推荐站点

  • 我爱发烧音乐我爱发烧音乐

    我爱发烧音乐囊括了从流行音乐到古典音乐多个类型的音乐作品,专栏推荐最新的音乐,提供音乐排名榜单!可供免费线上收听音乐,歌曲流畅,音效极佳! 网站提供的钢琴以及二胡专栏,可供收听者,陶冶情操,改善心情,是难得的轻音乐典藏!

    www.520fs.com
  • 世纪音乐网世纪音乐网

    世纪音乐网是专业的在线音乐试听MP3下载网站。歌曲总计30余万首,收录了网上最新歌曲和流行音乐,DJ舞曲,非主流音乐,经典老歌,劲舞团歌曲,搞笑歌曲,儿童歌曲,英文歌曲等。是您上网听歌的最佳网站。

    www.ssjj.com
  • 怒江大峡谷网怒江大峡谷网

    怒江大峡谷网内容包括:新闻、要闻、怒江报、视频、文化、民俗、人文、音乐、政务、公告、政策等地方信息。

    www.nujiang.cn
  • 杭州网杭州网

      杭州网是杭州地区唯一的新闻门户网站,由中共杭州市委宣传部、杭州日报报业集团和杭州广播电视集团共同组建的杭州网络传媒有限公司运营。

    www.hangzhou.com.cn
  • 深圳在线深圳在线

      深圳在线 www.szol.net是深圳本地最大、最早的地方生活资讯网站之一,网站名“深圳在线www.szol.net”由南方报业传媒集团编辑委员会总编辑、南方日报社总编辑、南方都市报总编辑、南方书画院名誉院长王春芙亲笔题名,深圳在线www.szol.net团队与深圳热线www.szonline.net、奥一网www.oeeee.com都源于全国最早成立于1996年的知名网络公司——深圳万用网。

    www.szol.net

鄂公网安备 42062502000001号