Java面试题:Java中能创建volatile数组吗?

Java 中能创建 volatile 数组吗?

答案:

Java 中可以创建 volatile 类型数组,不过只是一个指向数组的引用,而不是整个数组。如果改变引用指向的数组,将会受到 volatile 的保护,但是如果多个线程同时改变数组的元素,volatile 标示符就不能起到之前的保护作用了。

什么是 volatitle?

volatile 是 Java 的一个关键字,它是 Java 虚拟机提供的轻量级的同步机制,volatile 有三个特性:

(1)保证可见性

(2)不保证原子性

(3)禁止指令重排

下面通过示例演示,在不使用 volatitle 关键的情况下,线程修改了主内存中的数据,对主内存是不可见的,代码如下:

public class Demo {

    class Data {
        private int index = 0;

        public void setIndex(int index) {
            this.index = index;
        }

        public int getIndex() {
            return this.index;
        }
    }

    public Demo() {
        final Data data = new Data();

        // 在线程中修改数据
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                    System.out.println("线程开始执行...");
                    data.setIndex(10);
                    System.out.println("线程执行结束");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

        while(data.getIndex() == 0) {
            // 如果进入这里,表示不可见
        }

        System.out.println("index 是可见的");
    }

    public static void main(String[] args) {
        new Demo();
    }

}

输出结果如下:

线程开始执行...
线程执行结束

从上面的出处结果可知,即使我们在线程中通过 setIndex() 方法将 Data 中的 index 设置为 10,主线程中的 while 循环也没有结束。这是因为,线程中的修改对主线程是不可见的。此时,我们使用 volatile 关键字去修饰 index 属性,让他可见。代码如下:

public class Demo {

    class Data {
        private volatile int index = 0;

        public void setIndex(int index) {
            this.index = index;
        }

        public int getIndex() {
            return this.index;
        }
    }

    public Demo() {
        final Data data = new Data();

        // 在线程中修改数据
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                    System.out.println("线程开始执行...");
                    data.setIndex(10);
                    System.out.println("线程执行结束");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

        while(data.getIndex() == 0) {
            // 如果进入这里,表示不可见
        }

        System.out.println("index 是可见的");
    }

    public static void main(String[] args) {
        new Demo();
    }

}

输出结果如下:

线程开始执行...
线程执行结束
index 是可见的

volatile 修饰数组

前面使用 volatile 修饰单个值起到了作用,那么修饰数组呢?下面通过两个示例来演示。

示例一:volatile 对数组整体引用的效果

下面定义了一个 int 数组,且使用 volatile 修饰。而在 while 循环中一直比较数组的 hashCode 是否一直,不一致则跳出循环。当在线程中将变量 array 指向新的数组,此时当前数组的 hashCode 和 历史数组的 hashCode 就不一致,跳出循环。代码如下:

public class Demo {

    class Data {
        private volatile int[] array = new int[10];

        public void setArray(int[] newArray) {
            this.array = newArray;
        }

        public int[] getArray() {
            return this.array;
        }
    }

    public Demo() {
        final Data data = new Data();
        int hashCode = data.getArray().hashCode();

        // 在线程中修改数据
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                    System.out.println("线程开始执行...");
                    data.setArray(new int[]{});
                    System.out.println("线程执行结束");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

        while(data.getArray().hashCode() == hashCode) {
            // 如果进入这里,表示不可见
        }

        System.out.println("array 是可见的");
    }

    public static void main(String[] args) {
        new Demo();
    }

}

运行结果如下:

线程开始执行...
线程执行结束
array 是可见的

从上面示例可知,当我们修改数组变量执行其他数组时,volatile 是有用的。

示例二:volatile 对数组元素的效果

下面接着示例一的示例,将在线程中直接修改数组引用改为修改数组元素,代码如下:

public class Demo {

    class Data {
        private volatile int[] array = new int[10];

        public void setArray(int index, int value) {
            this.array[index] = value;
        }

        public int[] getArray() {
            return this.array;
        }
    }

    public Demo() {
        final Data data = new Data();
        int hashCode = data.getArray().hashCode();

        // 在线程中修改数据
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                    System.out.println("线程开始执行...");
                    // 修改数组元素
                    data.setArray(0, 10);
                    System.out.println("线程执行结束");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

        while(data.getArray().hashCode() == hashCode) {
            // 如果进入这里,表示不可见
        }

        System.out.println("array 是可见的");
    }

    public static void main(String[] args) {
        new Demo();
    }

}

输出结果如下:

线程开始执行...
线程执行结束

从输出结果可知,修改数组元素,volatile 不起作用。

游手好闲地学习,并不比学习游手好闲好。 —— 约翰·贝勒斯
0 不喜欢
说说我的看法 -
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号