import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 第9课贯穿场景：两个请求线程同时查看办件详情。
 * 用 Java 层面能观察到的事实，对照课件第一节的内存分区图：
 *   各线程的局部变量互不相干（栈私有）；两次 new 得到两个不同的对象（堆上分配）；
 *   两个对象的类是同一个 Class（类的结构只有一份）；同一个字符串字面量是同一个实例（入池）。
 * 运行：java reference/MemoryMapExample.java（Java 17 或 21）。最后一行输出“检查通过”。
 */
public class MemoryMapExample {

    static class Applicant {
        final String name;
        Applicant(String name) { this.name = name; }
    }

    // 模拟查询：A-002 查不到申请人
    static String queryName(String applyNo) {
        return applyNo.equals("A-001") ? "张三" : null;
    }

    static Applicant loadApplicant(String applyNo) {
        String name = queryName(applyNo);      // applyNo、name：当前线程栈帧里的局部变量
        if (name == null) name = "未知申请人";  // 字符串字面量：入池，所有线程共用一个实例
        return new Applicant(name);            // 新对象：在堆上分配
    }

    public static void main(String[] args) throws Exception {
        AtomicReference<Applicant> r1 = new AtomicReference<>();
        AtomicReference<Applicant> r2 = new AtomicReference<>();
        AtomicReference<Applicant> r3 = new AtomicReference<>();
        CountDownLatch start = new CountDownLatch(1);

        Thread t1 = new Thread(() -> { await(start); r1.set(loadApplicant("A-001")); }, "请求线程1");
        Thread t2 = new Thread(() -> { await(start); r2.set(loadApplicant("A-002")); }, "请求线程2");
        Thread t3 = new Thread(() -> { await(start); r3.set(loadApplicant("A-003")); }, "请求线程3");
        t1.start(); t2.start(); t3.start();
        start.countDown();
        t1.join(2000); t2.join(2000); t3.join(2000);

        Applicant a1 = r1.get(), a2 = r2.get(), a3 = r3.get();
        check(a1.name.equals("张三") && a2.name.equals("未知申请人"), "各线程按自己的 applyNo 算出各自的结果");
        check(a1 != a2 && a2 != a3, "每次 new 都是一个新对象");
        check(a1.getClass() == a2.getClass() && a2.getClass() == Applicant.class, "所有对象共用同一个类");
        check(a2.name == a3.name, "两个线程拿到的字面量 \"未知申请人\" 是同一个实例");
        check(a2.name == "未知申请人".intern(), "字面量就是入池的那个实例");
        System.out.println("线程1：" + a1.name + "，线程2：" + a2.name + "，线程3：" + a3.name);
        System.out.println("检查通过");
    }

    static void await(CountDownLatch latch) {
        try { latch.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
    }

    static void check(boolean ok, String message) {
        if (!ok) throw new AssertionError(message);
    }
}
