Java 并发基础:进程、线程、线程状态、synchronized、volatile 一篇讲清
Java 后端面试里并发几乎是必问模块。很多同学一开始学并发时会觉得概念很多进程、线程、线程状态、线程安全、synchronized、volatile、原子性、可见性、有序性……这些词单独看都不难但如果没有串起来很容易背得零散。这篇文章就从最基础的进程和线程开始逐步讲到 Java 线程状态、线程安全问题以及 synchronized 和 volatile 的区别。一、什么是进程什么是线程进程是操作系统分配资源的基本单位。线程是 CPU 调度执行的基本单位。可以简单理解为进程一个正在运行的程序 线程进程里面的一条执行路径比如你启动一个 Java 程序这个 Java 程序就是一个进程。这个程序里面可以有多个线程同时工作比如主线程、GC 线程、业务线程、线程池里的工作线程。二、进程和线程有什么区别对比进程线程资源拥有独立内存空间共享进程内存开销创建和切换开销大创建和切换开销较小通信进程间通信较复杂线程间共享变量更方便稳定性一个进程崩溃一般不影响其他进程一个线程异常可能影响整个进程面试可以这样回答进程是资源分配单位线程是 CPU 调度单位。同一进程内多个线程共享内存所以通信方便但也会带来线程安全问题。三、为什么要使用多线程使用多线程主要有几个原因提高 CPU 利用率 提高程序响应速度 处理并发请求 异步执行耗时任务比如 Java Web 服务中请求通常由线程池中的线程处理。如果只有一个线程多个请求只能排队执行吞吐量会很低。多线程可以让多个任务并发执行提高系统整体处理能力。四、线程越多越好吗不是。线程太多会带来很多问题线程切换开销大 占用内存 锁竞争加剧 系统负载过高每个线程都需要栈内存线程越多占用内存越多。另外CPU 核心数是有限的。线程太多时操作系统会频繁在线程之间切换反而降低性能。所以实际项目中不会无限创建线程而是使用线程池管理线程。五、Java 创建线程有哪几种方式常见说法有四种继承 Thread 实现 Runnable 实现 Callable FutureTask 使用线程池最简单的写法new Thread(() - { System.out.println(hello); }).start();不过实际开发中更推荐使用线程池而不是频繁手动 new Thread()。因为线程创建和销毁都有成本线程池可以复用线程也能统一控制并发数量。六、Runnable 和 Callable 有什么区别对比RunnableCallable返回值没有有异常不能直接抛 checked exception可以抛出方法run()call()示例CallableInteger task () - 1 1;Callable 通常配合 Future 或线程池使用可以拿到异步任务的执行结果。七、调用 start() 和 run() 有什么区别start() 是启动一个新线程。thread.start();JVM 会创建新线程并在线程中执行 run() 方法。如果直接调用thread.run();那只是普通方法调用不会创建新线程。面试重点启动线程必须调用 start()直接调用 run() 不会开启新线程。八、Java 线程有哪些状态Java 线程状态有 6 种NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED可以用下面这张图理解注意Java 里的 RUNNABLE 包括操作系统层面的“就绪”和“运行中”。九、BLOCKED、WAITING、TIMED_WAITING 有什么区别状态含义常见场景BLOCKED等待 synchronized 锁进入同步代码块但锁被别人占用WAITING无限期等待需要别人唤醒wait()、join()、LockSupport.park()TIMED_WAITING带时间的等待sleep(1000)、wait(1000)、join(1000)简单记BLOCKED等锁 WAITING一直等别人叫醒 TIMED_WAITING等一段时间十、sleep() 和 wait() 有什么区别这是 Java 并发超高频题。对比sleepwait所属Thread 类Object 类是否释放锁不释放锁释放锁使用位置任意地方必须在 synchronized 中唤醒方式时间到了自动醒notify/notifyAll 或超时重点记sleep 不释放锁 wait 会释放锁示例理解线程拿着锁 sleep别人还是进不来 线程执行 wait会释放锁让别人有机会进入同步代码块。十一、什么是线程安全线程安全指的是多个线程同时访问同一份共享数据时程序结果仍然正确。比如多个线程同时执行count;如果没有同步控制最后结果可能小于预期。因为 count 不是一个原子操作。十二、为什么 count 不是线程安全的count 看起来是一行代码但底层大概有三步1. 读取 count 2. count 1 3. 写回 count两个线程可能同时读到 count 0线程 A 读到 0加 1写回 1 线程 B 读到 0加 1写回 1执行了两次自增结果却是 1。这就是并发下的数据丢失。流程图十三、并发问题的根源有哪些常见有三个原子性 可见性 有序性问题含义原子性一个操作不可被打断可见性一个线程修改变量其他线程能及时看到有序性程序执行顺序不会因为重排序导致错误synchronized 可以保证原子性、可见性、有序性。volatile 主要保证可见性和一定的有序性但不保证复合操作的原子性。十四、synchronized 是什么synchronized 是 Java 内置锁也叫监视器锁。它可以修饰普通方法 静态方法 代码块示例synchronized (this) { count; }作用是同一时刻只允许一个线程进入同步代码块这样可以保证共享变量修改的线程安全。十五、synchronized 锁的是什么synchronized 锁的是对象。不同写法锁对象不同。普通同步方法public synchronized void method() { }锁的是当前实例对象this静态同步方法public static synchronized void method() { }锁的是当前类的 Class 对象。同步代码块synchronized (lock) { }锁的是括号里的 lock 对象。面试时一定要说清楚synchronized 不是锁代码而是锁对象。十六、synchronized 能保证什么synchronized 主要保证原子性 可见性 有序性进入 synchronized 前需要获得锁。退出 synchronized 时会把工作内存中的共享变量刷新到主内存。其他线程再进入 synchronized 时会从主内存读取最新值。所以它既能防止并发修改也能让其他线程看到最新结果。十七、volatile 是什么volatile 是 Java 关键字用来修饰变量。它主要保证可见性 禁止指令重排序示例private volatile boolean running true;一个线程修改running false;另一个线程能尽快看到变化。十八、volatile 能保证原子性吗不能。比如volatile int count 0; count;仍然不是线程安全的。因为 count 是读、改、写三个步骤。volatile 只能保证每次读到的是较新的值但不能保证这三个步骤不可被打断。如果要保证自增的原子性可以用AtomicInteger或者synchronized十九、volatile 常见使用场景是什么常见场景状态标记 双重检查锁定 DCL 停止线程标志比如停止线程private volatile boolean stop false; while (!stop) { // do something }另一个线程设置stop true;工作线程能及时看到变化并退出。二十、synchronized 和 volatile 有什么区别对比synchronizedvolatile原子性保证不保证可见性保证保证有序性保证一定程度保证是否阻塞可能阻塞不阻塞使用场景复合操作、临界区状态标记、配置开关面试回答synchronized 更重能保证原子性、可见性和有序性适合保护临界区 volatile 更轻量只保证可见性和禁止重排序适合一个线程写、多个线程读的状态标记不适合 count 这种复合操作。二十一、这一组怎么串起来讲可以这样回答进程是资源分配单位线程是 CPU 调度单位。同一进程内线程共享内存所以通信方便但也会带来线程安全问题。Java 创建线程可以继承 Thread、实现 Runnable、实现 Callable实际开发更推荐线程池。启动线程要调用 start直接调用 run 只是普通方法调用。Java 线程有 NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED 六种状态。线程安全问题主要来自原子性、可见性、有序性。 synchronized 锁的是对象能保证原子性、可见性、有序性 volatile 保证可见性和禁止重排序但不保证 count 这类复合操作的原子性。总结这一组可以按下面这条线来记进程是资源分配单位线程是 CPU 调度单位。多线程能提高并发处理能力但线程不是越多越好。启动线程要调用 start不是 run。Java 线程有 NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED 六种状态。BLOCKED 是等 synchronized 锁WAITING 是无限等待TIMED_WAITING 是限时等待。sleep 不释放锁wait 会释放锁。线程安全问题来自原子性、可见性、有序性。 synchronized 锁的是对象能保证原子性、可见性、有序性。 volatile 保证可见性和禁止重排序但不保证 count 的原子性。这一组重点背进程 vs 线程、start vs run、线程状态、sleep vs wait、线程安全三大问题、synchronized 锁对象、volatile 可见性、volatile 不保证原子性。 码字不易技术干货深度复盘如果这篇文章帮你看清了 MyBatis-Plus 查询的底层底细别忘了 点赞、关注、收藏 三连走一波支持作者不迷路更多底层源码干货持续输出中让我们一起学习面试知识拿到自己想要的offer