Java堆与栈核心区别及多线程场景的处理 一、堆Heap与栈Stack核心区别对比为了方便理解我用表格形式先梳理核心差异维度堆Heap栈Stack存储内容对象实例、数组、静态变量、常量池局部变量、方法参数、方法调用栈帧内存管理手动/自动GC回收分配复杂自动压栈/出栈内存连续且固定大小线程可见性全局共享所有线程可访问线程私有每个线程独立拥有自己的栈空间大小通常较大几G级别动态扩展通常较小几M级别固定上限访问效率较慢需通过指针间接访问较快直接通过栈顶指针操作异常类型OutOfMemoryError内存耗尽StackOverflowError栈深度超限二、多线程场景下的堆与栈行为分析1. 栈的线程私有特性绝对安全每个线程启动时JVM会为其分配独立的虚拟机栈栈中的局部变量、方法调用栈帧完全隔离public class StackThreadDemo { public static void main(String[] args) { // 线程1栈中保存自己的num变量 new Thread(() - { int num 1; try { Thread.sleep(1000); } catch (InterruptedException e) {} System.out.println(线程1的num num); // 输出1 }).start(); // 线程2栈中保存自己的num变量与线程1完全无关 new Thread(() - { int num 2; System.out.println(线程2的num num); // 输出2 }).start(); } }关键结论栈内存天然线程安全不存在多线程竞争问题因为每个线程的栈是独立的。2. 堆的共享特性线程不安全场景堆是所有线程共享的内存区域当多个线程访问堆中的对象时会出现线程安全问题场景1多线程修改共享对象属性public class HeapThreadDemo { // 共享对象存储在堆中 static class Counter { int count 0; } public static void main(String[] args) throws InterruptedException { Counter counter new Counter(); // 10个线程同时修改堆中的count属性 for (int i 0; i 10; i) { new Thread(() - { for (int j 0; j 1000; j) { // 非原子操作会出现线程安全问题 counter.count; } }).start(); } Thread.sleep(2000); System.out.println(最终count值 counter.count); // 大概率小于10000 } }问题原因count包含读取、加1、写入三个步骤多个线程可能同时读取旧值导致覆盖。场景2多线程访问共享静态变量静态变量存储在堆的方法区中同样属于共享资源public class StaticHeapDemo { // 静态变量存储在堆的方法区 static int staticNum 0; public static void main(String[] args) throws InterruptedException { for (int i 0; i 5; i) { new Thread(() - { staticNum 10; }).start(); } Thread.sleep(1000); System.out.println(staticNum最终值 staticNum); // 可能小于50 } }3. 堆的线程安全解决方案针对堆中共享资源的竞争问题常用解决方式方案示例代码片段适用场景synchronized锁synchronized (counter) { counter.count; }通用场景保证原子性原子类AtomicInteger count new AtomicInteger(0); count.incrementAndGet();简单数值操作性能更高Lock锁ReentrantLock lock new ReentrantLock(); lock.lock(); try { ... } finally { lock.unlock(); }复杂场景支持公平锁等特性三、多线程下堆与栈的典型异常栈异常StackOverflowError当线程调用方法的深度超过栈的最大容量时抛出比如递归调用无终止条件public class StackOverflowDemo { public static void recursive() { recursive(); // 无限递归 } public static void main(String[] args) { recursive(); // 抛出StackOverflowError } }注意每个线程的栈独立一个线程栈溢出不会影响其他线程。堆异常OutOfMemoryError当堆内存无法分配新对象时抛出比如创建大量对象public class OutOfMemoryDemo { public static void main(String[] args) { ListObject list new ArrayList(); while (true) { list.add(new Object()); // 不断创建对象最终抛出OutOfMemoryError } } }注意堆是共享的一个线程耗尽堆内存会导致所有线程无法分配新对象。