java中奇怪的可见性问题

写个可见性的测试代码,发现了一个奇怪的情况

public class VolatileTest2 {
 
    public VolatileTest2(int i) {
        i++;
    }
    static boolean a = false;
 
    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    if (a) {
                        System.out.println("可见");
                        break;
                    }
                    // 非常的奇怪,new Object和一些自定义类时,没什么问题
                    // new String和Integer等对象时,竟然打印出来”可见“了,竟然保证的了可见性
                    // 可见:String、Integer、HashMap、Error、Boolean
                    // 不可见:自定义类、Object、ArrayList
                    new Boolean(true);
                }
            }
        }).start();
 
        Thread.sleep(2000);
 
        new Thread(new Runnable() {
            @Override
            public void run() {
                a = true;
            }
        }).start();
    }

为什么在线程的while循环里new一些类的时候就可见了,new另外一些类的时候又不可见了。

该回答引用自ChatGPT

这是因为Java的内存模型规定,当一个线程对一个变量进行修改时,其他线程可能无法立即看到这个变量的最新值,这就是可见性问题。为了保证可见性,需要使用volatile关键字或者synchronized关键字进行同步。
在这个测试代码中,线程1在while循环中尝试读取变量a的值,如果a为true,则输出“可见”并退出循环。而线程2在2秒后将a的值修改为true。由于没有使用同步机制,线程1可能无法立即看到a的最新值。
但是,当线程1在while循环中new String、Integer、HashMap、Error、Boolean等对象时,由于Java中的字符串常量池、整数常量池和一些常用对象可能会被预先加载,所以这些对象的创建可能会触发一些JVM优化,从而使得变量a的最新值能够被线程1立即看到,从而输出“可见”。
而对于自定义类、Object、ArrayList等对象,则没有这些优化,所以线程1无法立即看到变量a的最新值,输出“不可见”。