什么是Java并发编程基础概念详解_新手快速理解多线程原理

Java并发编程核心是保障共享资源下的正确性,需直面CPU缓存、指令重排与线程切换导致的不可预测性;进程是OS内存分配单位,线程是JVM调度最小单位,共享堆内存;并发是单核时间片轮转,並行是多核真正同时执行;裸线程缺乏管理易致资源浪费与异常失控,必须用ExecutorService;synchronized保证原子性、可见性与互斥,volatile仅保证可见性与禁止重排,不保证原子性;wait/notify须用while循环重检条件以防虚假唤醒。

Java并发编程不是“多开几个线程就完事”,而是围绕「共享资源下的正确性」展开的——你写的代码在单线程下跑得通,放到多线程里很可能出错,根本原因在于:多个线程同时读写同一个变量时,CPU缓存、指令重排、线程切换这些底层机制会让结果变得不可预测。

进程、线程、并发、并行,到底谁在调度谁?

别被术语绕晕。记住这四句硬核事实:

  • 进程是操作系统分配内存和文件句柄的单位,比如启动两次 java -jar app.jar 就是两个独立进程,互不共享堆内存;
  • 线程是 JVM 调度执行的最小单位,一个进程(如你的 Java 应用)默认就有 mainReference HandlerFinalizer 等多个线程,它们共享同一块堆内存;
  • 并发是单核 CPU 上靠时间片轮转“假装同时干活”,比如 1ms 切一次线程,人眼看不出卡顿;
  • 并行是真·多核同时跑,比如 8 核机器上 8 个线程各自独占一个核心,但前提是你的代码没因锁或竞争被串行化。

新手常误以为“开了 10 个线程就等于快了 10 倍”,其实只要它们争抢同一把锁、频繁同步或阻塞在 I/O,实际吞吐可能比单线程还差。

为什么直接 new Thread().start() 很危险?

因为裸线程缺乏生命周期管理、复用能力和错误兜底——它就像随手打的出租车,用完就扔,既浪费资源又难追踪。

立即学习“Java免费学习笔记(深入)”;

  • 每次 new Thread() 都要创建栈空间(默认 1MB)、触发 JVM 线程注册、系统调用,开销远高于对象创建;
  • 线程异常未捕获会静默终止,main 线程不知道,监控也收不到告警;
  • 没有拒绝策略,突发流量可能直接 OutOfMemoryError: unable to create new native thread
  • 无法统一设置守护线程、优先级、UncaughtExceptionHandler。

所以生产环境必须用 ExecutorService,哪怕只是最简单的 Executors.newFixedThreadPool(4),也比裸线程靠谱得多。

synchronized 和 volatile 到底管什么?

它们解决的是两类完全不同的问题,混用等于白加:

  • synchronized 是“原子+可见+互斥”三合一:进块前加锁,出块后释放,期间其他线程进不来,且保证块内对共享变量的修改对后续进入的线程立即可见;
  • volatile 只管“可见性+禁止指令重排”,不保证原子性——比如 counter++(读-改-写三步)用 volatile 修饰 counter 依然会丢数据;
  • 常见坑:volatile boolean running = true; 配合 while(running) 做线程开关是对的;但想用它实现计数器、状态机跳转,大概率翻车。

真正需要原子操作,请用 AtomicIntegerAtomicReferenceReentrantLock,别自己造轮子。

线程间通信,wait/notify 为什么总失效?

失效主因就两个字:条件判断。90% 的 bug 出在没用 while 而用 if 检查条件:

// ❌ 错误:if 可能导致虚假唤醒后直接往下走
synchronized (lock) {
    if (queue.isEmpty()) {
        lock.wait(); // 被唤醒后不重新检查,直接消费空队列!
    }
    queue.poll();
}

// ✅ 正确:必须用 while 循环重检条件
synchronized (lock) {
    while (queue.isEmpty()) {
        lock.wait();
    }
    queue.poll();
}

另外注意:wait() 必须在 synchronized 块内调

用,notify() 只唤醒一个等待线程(可能不是你需要的那个),有确定顺序需求请用 notifyAll() 或更现代的 Condition

并发编程最难的从来不是语法,而是构建“可推理的执行模型”——当你写下一行共享变量读写时,得清楚知道它在 CPU 缓存、JVM 内存模型、操作系统调度层上分别会发生什么。不理解这些,再多的 synchronized 也救不了逻辑漏洞。