欢迎访问欧博亚洲(Allbet Game)!

首页科技正文

大同大学学报:Java内存可见性volatile

admin2020-04-1354

概述

JMM规范指出,每一个线程都有自己的事情内存(working memory),当变量的值发生变化时,先更新自己的事情内存,然后再拷贝到主存(main memory),这样其他线程就能读取到更新后的值了。
注重:事情内存和主存是JMM规范里抽象的观点,在JVM的内存模子下,可以将CPU缓存对应作线程事情内存,将JVM堆内存对应主存。

写线程更新后的值何时拷贝到主存?读线程何时从主存中获取变量的最新值?hotspotJVM中引入volatile关键字来解决这些问题,当某个变量被volatile关键字修饰后,多线程对该变量的操作都将直接在主存中举行。在CPU时钟顺序上,某个写操作执行完成后,后续的读操作一定读取的都是最新的值。

内存可见性带来的问题

如下代码片断,写线程每隔1秒递增共享变量counter,读线程是个死循环,若是读线程始终能读取到counter的最新值,那么最终的输出应该是 12345。

public class App {
    // 共享变量
    static int counter = 0;

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            int temp = 0;
            while (true) {
                if (temp != counter) {
                    temp = counter;
                    // 打印counter的值,期望打印 12345
                    System.out.print(counter);
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                counter++;
                // 守候1秒,给读线程足够的时间读取变量counter的最新值
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            // 退出程序
            System.exit(0);
        });

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

在没有volatile的情况下,现实的输出结构如下:

1

Process finished with exit code 0

通过volatile解决问题

将共享变量用volatile关键字修饰即可,如下:

// 共享变量
static volatile int counter = 0;

再次执行程序,输出效果如下:

12345

Process finished with exit code 0

综上,volatile关键字使得各个线程对共享变量的操作变得一致。在非volatile字段上做更新操作时,无法保证其修改后的值何时从事情内存(CPU缓存)刷新到主存。对于非volatile字段的读操作也是云云,无法保证线程何时从主存中读取最新的值。

volatile无法保证线程平安性

如下代码片断,多个线程同时递增一个计数器:

public class App {
    // 共享变量
    static volatile int counter = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                counter++;
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                counter++;
            }
        });

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

        System.out.println("总和:" + counter);
    }

输入效果:

总和:12374

若是volatile能保证线程平安,那么输出效果应该是20000,但上面的代码输出12374,以是说,volatile不能解决线程平安(thread)的问题。
以是,照样要通过其他手段来解决多线程平安的问题,好比synchronized。

volatile和synchronized的区别

在上述的代码示例中,我们并没有涉及到多线程竞态(race condition)的问题,焦点点是“多线程情况下,对共享变量的写入若何被其他线程实时读取到”。
synchronized关键字是Java中最常用的锁机制,保证临界区(critical section)中的代码在同一个时间只能有一个线程执行,临界区中使用的变量都将直接从主存中读取,对变量的更新也会直接刷新到主存中。以是行使synchronized也能解决内存可见性问题。
代码如下:

public class App {
    // 共享变量
    static int counter = 0;

    public static void main(String[] args) {
        // 读取变量的线程
        Thread readThread = new Thread(() -> {
            int temp = 0;
            while (true) {
                synchronized (App.class) {
                    if (temp != counter) {
                        temp = counter;
                        // 打印counter的值,期望打印 12345
                        System.out.print(counter);
                    }
                }
            }
        });

        // 修改变量的线程
        Thread writeThread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                synchronized (App.class) {
                    counter++;
                }

                // 守候1秒,给读线程足够的时间读取变量counter的最新值
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.exit(0);
        });

        readThread.start();
        writeThread.start();
    }
}

运行,输入效果:

12345

Process finished with exit code 0

虽然通过synchronized也能解决内存可见性的问题,然则这个解决方案也带来了其他问题,好比性能会比较差。

总结

多线程可以提升程序的运行速率,充分行使多核CPU的算力,但多线程也是“恶魔”,会给程序员带来许多问题,好比本文中的内存可见性问题。volatile可以使变量的更新实时刷新到主存,变量的读取也是直接从主存中获取,保证了数据的内存一致性。然则volatile不是用来解决线程平安问题的,无法替换锁机制。

参考:
[1] Java Memory Model - Visibility problem, fixing with volatile variable
[2] Guide to the Volatile Keyword in Java
[3] Managing volatility
[4] Java Volatile Keyword
[5] Thread and Locks

,

sunbet

www.0379st.com信誉来自于每一位客户的口碑,Sunbet贴心的服务,让你尊享贵宾通道,秒速提现,秒速到账,同行业中体验最佳。

转载声明:本站发布文章及版权归原作者所有,转载本站文章请注明文章来源:欧博亚洲(Allbet Game)!

本文链接:https://www.qzkaishanjx.com/post/680.html

网友评论

最新评论

  • 环球UG官方网 10/26 说:

    Allbet官网欢迎进入Allbet官网(Allbet Game):www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。运动,阅读,两大幸事

  • 环球UG官方网 10/26 说:

    Allbet官网欢迎进入Allbet官网(Allbet Game):www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。运动,阅读,两大幸事

  • 联博API 10/25 说:

    联博统计接口www.326681.com采用以太坊区块链高度哈希值作为统计数据,联博以太坊统计数据开源、公平、无任何作弊可能性。联博统计免费提供API接口,支持多语言接入。挺好,再多一些特点

  • AllbetGmaing官网 10/25 说:

    欧博注册欢迎进入欧博注册(Allbet Game):www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。回味无穷

  • 环球UG网址 10/24 说:

    欧博亚洲客户端下载欢迎进入欧博亚洲客户端下载(Allbet Game):www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。作者大大好棒

  • 欧博亚洲客户端 10/24 说:

    欧博手机版下载欢迎进入欧博手机版下载(Allbet Game):www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。心情阳光明媚啊

  • UG环球手机版下载 10/23 说:

    联博开奖www.326681.com采用以太坊区块链高度哈希值作为统计数据,联博以太坊统计数据开源、公平、无任何作弊可能性。联博统计免费提供API接口,支持多语言接入。熟悉的笔风~

  • 环球UG代理 10/23 说:

    apple developer enterprise account for rentproviding apple enterprise developer accounts for rent, rent your own enterprise account for app signing. with high quality, stable performance and affordable price.我是死忠粉了