volatile修饰引用类型ArrayList的诡异问题

来源:13-1 volatile-

qq_凛冬将至_9

2022-02-01

老师新年好,在复习多线程这块时,看到一个程序的运行让人很疑惑,结合之前学的知识没法解释。程序如下:

package ...

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class Test_00_monitor_list_to_five_issue {

    // 1
    volatile List lists = new ArrayList();

    public void add(Object o) {
        lists.add(o);
    }

    public int size() {
        return lists.size();
    }

    public static void main(String[] args) {
        Test_00_monitor_list_to_five_issue c = new Test_00_monitor_list_to_five_issue();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                c.add(new Object());
                System.err.println("add " + i); // 2
//                sleepMilliSeconds(500); // 3
            }
        }, "t1").start();

        new Thread(() -> {
            while (true) {
                if (c.size() == 5) {
                    break;
                }
            }
            System.err.println("t2 结束");
        }, "t2").start();
    }
    // 为了让调用处不用捕获异常,使得代码更简洁
    public static void sleepMilliSeconds(int s) {
        try {
            TimeUnit.MILLISECONDS.sleep(s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

我试了下不同的处理方式:

  1. 1处不加volatile,不管2、3两处是否放开注释,t2线程都不会结束
  2. 1处加volatile,2、3放开一处的注释,t2都能正常结束
  3. 即使1处加了volatile,2、3两处都注释掉的情况下,t2线程也不能结束

令我疑惑的是,volatile一般都修饰值类型,修饰引用类型,引用始终没改变,改变的只是内部的属性,在某些情况下(这里2、3放开一处的注释)其中的size字段可见了,这是什么原理?另外第三个操作volatile为啥又失效了?2、3两处的代码对volatile修饰的ArrayList中的size字段可见性有什么影响?
实在整懵了,向老师求救/(ㄒoㄒ)/~~

写回答

2回答

悟空

2022-04-02


我在实验的时候,加了volatile,但是只放开2,不放开3,t2依然无法停止。

第3个处理方式,未必是volatile失效,也许是t1运行太快,t2还没判断的时候,t1就跑完10次循环了。

理论上说,其实2和3的代码,对size的可见性并没有影响,而是因为sleep的时候,Java会去尽量保证可见性,因此能看到size的变化。

0
1
qq_凛冬将至_9
非常感谢!
2023-01-14
共1条回复

张婧仪

5天前

我来回答你的疑问

问题一,如果不加Volatile,没有可见性,所以t2不会停止

问题二三,加了Volatile,如果打开2,注释3,有时候可见,有时候不可见。如果注释2,打开3,一直可见。这是因为println和睡眠使得T1执行变慢,t2才有可能捕捉到,因为t2并不是一直在运行,由于CPU的调度。

=======

由于Volatile修饰的是List对象,而List包含size,size改变,list对象也改变,所以有可见性

0
0

线程八大核心+Java并发原理及企业级并发解决方案

完整的并发知识网络+丰富的工作内容分享+50余道并发高频面试题

2512 学习 · 940 问题

查看课程