Java 并发基础:进程、线程、线程状态、synchronized、volatile 一篇讲清

Java 后端面试里,并发几乎是必问模块。

很多同学一开始学并发时,会觉得概念很多:进程、线程、线程状态、线程安全、synchronized、volatile、原子性、可见性、有序性......

这些词单独看都不难,但如果没有串起来,很容易背得零散。

这篇文章就从最基础的进程和线程开始,逐步讲到 Java 线程状态、线程安全问题,以及 synchronized 和 volatile 的区别。


一、什么是进程?什么是线程?

进程是操作系统分配资源的基本单位。

线程是 CPU 调度执行的基本单位。

可以简单理解为:

进程:一个正在运行的程序 线程:进程里面的一条执行路径

比如你启动一个 Java 程序,这个 Java 程序就是一个进程。

这个程序里面可以有多个线程同时工作,比如主线程、GC 线程、业务线程、线程池里的工作线程。


二、进程和线程有什么区别?

对比 进程 线程
资源 拥有独立内存空间 共享进程内存
开销 创建和切换开销大 创建和切换开销较小
通信 进程间通信较复杂 线程间共享变量更方便
稳定性 一个进程崩溃一般不影响其他进程 一个线程异常可能影响整个进程

面试可以这样回答:

进程是资源分配单位,线程是 CPU 调度单位。同一进程内多个线程共享内存,所以通信方便,但也会带来线程安全问题。


三、为什么要使用多线程?

使用多线程主要有几个原因:

提高 CPU 利用率 提高程序响应速度 处理并发请求 异步执行耗时任务

比如 Java Web 服务中,请求通常由线程池中的线程处理。

如果只有一个线程,多个请求只能排队执行,吞吐量会很低。

多线程可以让多个任务并发执行,提高系统整体处理能力。


四、线程越多越好吗?

不是。

线程太多会带来很多问题:

线程切换开销大 占用内存 锁竞争加剧 系统负载过高

每个线程都需要栈内存,线程越多,占用内存越多。

另外,CPU 核心数是有限的。线程太多时,操作系统会频繁在线程之间切换,反而降低性能。

所以实际项目中不会无限创建线程,而是使用线程池管理线程。


五、Java 创建线程有哪几种方式?

常见说法有四种:

继承 Thread 实现 Runnable 实现 Callable + FutureTask 使用线程池

最简单的写法:

java 复制代码
new Thread(() -> {
     System.out.println("hello"); 
    }).start();

不过实际开发中更推荐使用线程池,而不是频繁手动 new Thread()。

因为线程创建和销毁都有成本,线程池可以复用线程,也能统一控制并发数量。


六、Runnable 和 Callable 有什么区别?

对比 Runnable Callable
返回值 没有
异常 不能直接抛 checked exception 可以抛出
方法 run() call()

示例:

java 复制代码
Callable<Integer> task = () -> 1 + 1;

Callable 通常配合 Future 或线程池使用,可以拿到异步任务的执行结果。


七、调用 start() 和 run() 有什么区别?

start() 是启动一个新线程。

java 复制代码
thread.start();

JVM 会创建新线程,并在线程中执行 run() 方法。

如果直接调用:

java 复制代码
thread.run();

那只是普通方法调用,不会创建新线程。

面试重点:

启动线程必须调用 start(),直接调用 run() 不会开启新线程。


八、Java 线程有哪些状态?

Java 线程状态有 6 种:

NEW

RUNNABLE

BLOCKED

WAITING

TIMED_WAITING

TERMINATED

可以用下面这张图理解:

注意:

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 并发超高频题。

对比 sleep wait
所属 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 内置锁,也叫监视器锁。

它可以修饰:

普通方法 静态方法 代码块

示例:

java 复制代码
synchronized (this) { 
    count++; 
    }

作用是:

同一时刻只允许一个线程进入同步代码块

这样可以保证共享变量修改的线程安全。


十五、synchronized 锁的是什么?

synchronized 锁的是对象。

不同写法锁对象不同。

普通同步方法:

java 复制代码
public synchronized void method() { }

锁的是当前实例对象:

this

静态同步方法:

java 复制代码
public static synchronized void method() { }

锁的是当前类的 Class 对象。

同步代码块:

java 复制代码
synchronized (lock) { }

锁的是括号里的 lock 对象。

面试时一定要说清楚:

synchronized 不是锁代码,而是锁对象。


十六、synchronized 能保证什么?

synchronized 主要保证:

原子性 可见性 有序性

进入 synchronized 前需要获得锁。

退出 synchronized 时,会把工作内存中的共享变量刷新到主内存。

其他线程再进入 synchronized 时,会从主内存读取最新值。

所以它既能防止并发修改,也能让其他线程看到最新结果。


十七、volatile 是什么?

volatile 是 Java 关键字,用来修饰变量。

它主要保证:

可见性 禁止指令重排序

示例:

java 复制代码
private volatile boolean running = true;

一个线程修改:

java 复制代码
running = false;

另一个线程能尽快看到变化。


十八、volatile 能保证原子性吗?

不能。

比如:

java 复制代码
volatile int count = 0; 
count++;

仍然不是线程安全的。

因为 count++ 是读、改、写三个步骤。

volatile 只能保证每次读到的是较新的值,但不能保证这三个步骤不可被打断。

如果要保证自增的原子性,可以用:

AtomicInteger

或者:

synchronized


十九、volatile 常见使用场景是什么?

常见场景:

状态标记 双重检查锁定 DCL 停止线程标志

比如停止线程:

java 复制代码
private volatile boolean stop = false;
while (!stop) { 
    // do something 
    }

另一个线程设置:

java 复制代码
stop = true;

工作线程能及时看到变化并退出。


二十、synchronized 和 volatile 有什么区别?

对比 synchronized volatile
原子性 保证 不保证
可见性 保证 保证
有序性 保证 一定程度保证
是否阻塞 可能阻塞 不阻塞
使用场景 复合操作、临界区 状态标记、配置开关

面试回答:

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!

相关推荐
weixin_446729162 小时前
java中class类没有打进war包中
java
郝学胜-神的一滴2 小时前
Python 高级编程 019:类变量与实例变量彻底解析
开发语言·python·程序人生·软件构建
哭哭啼2 小时前
pgSql 事务篇
java·数据库·postgresql
架构源启2 小时前
Spring AI进阶系列(17)- 未来展望与职业发展:Java 工程师迈向 AI 工程化与智能体架构的路线图
java·人工智能·spring
我登哥MVP2 小时前
Spring Boot 从“会用”到“精通”:SpringBoot MVC 请求处理全流程
java·spring boot·后端·spring·mvc·maven·intellij-idea
Thomas_YXQ2 小时前
Unity3D Addressable 深度优化热更性能消耗
开发语言·3d·unity·微信
aini_lovee2 小时前
C# 快递单打印系统(万能套打系统)
开发语言·c#
我登哥MVP2 小时前
Spring Boot 从“会用”到“精通”:ReturnValueHandler原理
java·spring boot·后端·spring·java-ee·maven·intellij-idea
snow@li2 小时前
数据库:MySQL vs PostgreSQL 详尽对比(2026版)
java·mysql·postgresql