
一、前言线程这一块很多人学的时候是“会写几个例子”但一到开发和面试就容易乱start()和run()容易混sleep()、join()、yield()分不清synchronized、Lock说不透Callable、Future、FutureTask容易串线程池只会Executors.newFixedThreadPool()不会讲底层参数一问线程安全、死锁、线程池参数、拒绝策略就开始发懵这篇文章想做的事很简单把 Java 线程相关知识整理成一个完整框架覆盖学习、开发、面试三个场景。二、线程知识总框架Java 线程相关内容建议按下面这条主线去理解1. 基础概念进程和线程并发和并行为什么要有线程2. 线程创建与常用方法ThreadRunnableCallableFutureTaskstart()、run()、sleep()、join()、interrupt()3. 线程安全什么是线程安全共享资源、临界区、竞态条件synchronizedLock原子类4. 线程协作与高级并发工具wait()/notify()/notifyAll()CountDownLatchCyclicBarrierSemaphore5. 线程池为什么要用线程池ExecutorServiceThreadPoolExecutor七大参数工作流程拒绝策略6. 面试高频专题synchronized和Lock区别sleep()和wait()区别Runnable和Callable区别线程池参数与执行流程死锁及排查CAS 和AtomicIntegervolatile三、什么是线程为什么要学线程1. 什么是线程线程是程序中的一条执行路径。可以这样理解进程一个运行中的程序线程进程中的执行单位比如一个聊天软件一个线程负责接收消息一个线程负责显示界面一个线程负责下载图片所以线程存在的意义就是让程序能够并发处理多个任务。2. 为什么要学线程在实际开发中线程非常常见Web 服务同时处理多个请求文件上传、下载消息队列异步消费定时任务批量数据处理高并发库存扣减、订单处理所以线程不是“只在面试里出现的知识点”而是 Java 后端开发中的基础能力。四、线程的三种常见创建方式1. 继承 Threadclass MyThread extends Thread { Override public void run() { System.out.println(线程执行了 Thread.currentThread().getName()); } } public class Main { public static void main(String[] args) { MyThread t new MyThread(); t.start(); } }特点简单直接但 Java 单继承扩展性差实际开发中不太推荐2. 实现 Runnableclass MyRunnable implements Runnable { Override public void run() { System.out.println(线程执行了 Thread.currentThread().getName()); } } public class Main { public static void main(String[] args) { Thread t new Thread(new MyRunnable()); t.start(); } }特点更常用任务和线程分离更适合实际开发核心理解这句代码Thread t new Thread(new MyRunnable());意思是new MyRunnable()创建任务对象new Thread(...)创建线程对象把任务交给线程执行3. 实现 Callable FutureTask如果任务需要返回值就不能只用Runnable而是要用Callable。import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class Main { public static void main(String[] args) throws Exception { CallableInteger callable () - { int sum 0; for (int i 1; i 100; i) { sum i; } return sum; }; FutureTaskInteger futureTask new FutureTask(callable); Thread t new Thread(futureTask); t.start(); Integer result futureTask.get(); System.out.println(结果 result); } }面试回答模板Runnable没有返回值Callable有返回值且可以抛异常FutureTask可以把Callable包装成线程能执行的对象同时还能通过get()获取结果。五、线程常用方法总结1.start()和run()class MyThread extends Thread { Override public void run() { System.out.println(当前线程 Thread.currentThread().getName()); } } public class Main { public static void main(String[] args) { MyThread t new MyThread(); t.run(); // 普通方法调用 t.start(); // 启动新线程 } }区别run()普通方法调用不会开启新线程start()启动新线程JVM 会自动调用run()面试高频一句话线程启动必须调用start()不能直接调run()。2.sleep()try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }作用让当前线程休眠指定时间。特点是Thread的静态方法不会释放锁3.join()public class Main { public static void main(String[] args) throws Exception { Thread t new Thread(() - { for (int i 1; i 5; i) { System.out.println(子线程 i); } }); t.start(); t.join(); System.out.println(主线程继续执行); } }作用让当前线程等待目标线程执行完毕。4.interrupt()public class Main { public static void main(String[] args) { Thread t new Thread(() - { while (!Thread.currentThread().isInterrupted()) { } System.out.println(线程被中断); }); t.start(); t.interrupt(); } }作用发送中断信号不是强行杀死线程。六、线程安全问题1. 什么是线程安全如果一段代码在多线程环境下不管线程怎么调度运行结果都始终正确那它就是线程安全的。2. 线程安全问题出现的条件一般要满足三个条件有多个线程有共享资源有修改操作3. 经典示例卖票问题class Ticket implements Runnable { private int count 100; Override public void run() { while (count 0) { count--; System.out.println(Thread.currentThread().getName() 卖出一张票剩余 count); } } }这段代码在多线程下会出现重复卖票票数错误负数票原因是count--不是原子操作。七、synchronized 详解1. synchronized 的作用synchronized用于解决线程安全问题本质是同一时刻只允许一个线程进入同步代码。2. 同步代码块class Ticket implements Runnable { private int count 100; Override public void run() { while (true) { synchronized (this) { if (count 0) { break; } count--; System.out.println(Thread.currentThread().getName() 卖票剩余 count); } } } }synchronized(this)锁的是谁锁的是当前对象。只有多个线程操作的是同一个对象时这把锁才有意义。3. 同步方法public synchronized void sell() { if (count 0) { count--; } }本质同步实例方法本质上等价于public void sell() { synchronized (this) { if (count 0) { count--; } } }4. 同步方法和同步代码块怎么选同步方法适合整个方法都需要同步同步代码块适合只想锁关键几行代码更灵活面试表达同步代码块通常更推荐因为锁粒度更小性能更好灵活性更高。八、Lock 锁详解synchronized是内置锁Lock是显示锁最常用的是ReentrantLock。1. 基本用法import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class Ticket implements Runnable { private int count 100; private Lock lock new ReentrantLock(); Override public void run() { while (true) { lock.lock(); try { if (count 0) { break; } count--; System.out.println(Thread.currentThread().getName() 卖票剩余 count); } finally { lock.unlock(); } } } }2. 为什么必须 try-finally因为Lock需要手动释放锁lock.lock(); try { // 临界区 } finally { lock.unlock(); }防止异常时锁没有释放。3. synchronized 和 Lock 区别synchronizedJava 内置自动加锁和释放锁写法简单Lock需要手动加锁、解锁更灵活支持tryLock()、可中断锁等高级功能面试标准回答synchronized适合简单同步场景Lock更灵活适合复杂并发控制。九、线程池为什么重要开发里很少直接大量new Thread()原因是创建线程有开销数量不可控线程不能复用可能导致系统资源耗尽所以一般都使用线程池。十、线程池基础ExecutorService最简单用法import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Main { public static void main(String[] args) { ExecutorService pool Executors.newFixedThreadPool(3); pool.submit(() - { System.out.println(Thread.currentThread().getName() 执行任务); }); pool.shutdown(); } }核心思想你不再自己创建线程而是把任务提交给线程池由线程池统一管理线程。十一、ThreadPoolExecutor 七大参数详解线程池面试里最重要的知识点之一就是ThreadPoolExecutor的 7 个参数ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueRunnable workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler )1.corePoolSize核心线程数2.maximumPoolSize最大线程数3.keepAliveTime非核心线程空闲存活时间4.unit时间单位5.workQueue任务队列6.threadFactory线程工厂7.handler拒绝策略标准示例import java.util.concurrent.*; public class Main { public static void main(String[] args) { ThreadPoolExecutor pool new ThreadPoolExecutor( 2, 4, 60, TimeUnit.SECONDS, new ArrayBlockingQueue(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy() ); } }线程池执行流程假设核心线程数 2最大线程数 4队列容量 3执行顺序先创建核心线程处理任务核心线程满了新任务进入队列队列满了再创建非核心线程最大线程也满了执行拒绝策略面试高频一句话线程池处理任务的顺序是先核心线程再队列再非核心线程最后拒绝策略。十二、Executors 和 ThreadPoolExecutor 区别很多人会问既然有Executors.newFixedThreadPool()为什么还要用ThreadPoolExecutor1. Executors工具类快速创建线程池适合入门和简单 demo2. ThreadPoolExecutor线程池核心实现类参数可控更适合实际开发面试回答模板Executors本质上是对ThreadPoolExecutor的封装但很多细节参数被隐藏了。实际开发中更推荐直接使用ThreadPoolExecutor因为线程数、队列大小、拒绝策略都更清晰、更可控。十三、Future、Callable、FutureTask 关系这是一个特别容易混的点。1. Callable有返回值的任务2. Future未来结果对象可以通过get()获取返回值3. FutureTask可以包装Callable同时也能作为Runnable被线程执行示例import java.util.concurrent.*; public class Main { public static void main(String[] args) throws Exception { ExecutorService pool Executors.newFixedThreadPool(2); FutureInteger future pool.submit(() - { int sum 0; for (int i 1; i 100; i) { sum i; } return sum; }); System.out.println(结果 future.get()); pool.shutdown(); } }十四、面试高频问题总结1.sleep()和wait()区别sleep()Thread的静态方法线程休眠不释放锁wait()Object的方法线程进入等待状态会释放锁必须在同步代码块或同步方法中使用2.synchronized和Lock区别前面已经讲过面试时要重点强调自动/手动释放锁灵活性高级能力如tryLock()3.Runnable和Callable区别Runnable无返回值不能直接抛受检异常Callable有返回值可以抛异常4. 什么是死锁多个线程互相持有对方需要的锁并且彼此等待导致谁都无法继续执行。示例public class DeadLockDemo { private static final Object lockA new Object(); private static final Object lockB new Object(); public static void main(String[] args) { new Thread(() - { synchronized (lockA) { System.out.println(线程1拿到A); synchronized (lockB) { System.out.println(线程1拿到B); } } }).start(); new Thread(() - { synchronized (lockB) { System.out.println(线程2拿到B); synchronized (lockA) { System.out.println(线程2拿到A); } } }).start(); } }死锁避免思路保证加锁顺序一致减少锁嵌套使用tryLock()5. 什么是 volatilevolatile用来保证变量对多线程的可见性。它不能保证复合操作的原子性比如count仍然线程不安全。6. 什么是 CAS 和原子类CASCompare And Swap是一种无锁并发机制。Java 中常见原子类AtomicIntegerAtomicLongAtomicReference示例import java.util.concurrent.atomic.AtomicInteger; public class Main { public static void main(String[] args) { AtomicInteger count new AtomicInteger(0); count.incrementAndGet(); System.out.println(count.get()); } }十五、学习建议线程怎么学才不乱我个人建议按这个顺序来第一步先打基础ThreadRunnablestart()/run()sleep()/join()第二步搞懂线程安全共享资源线程不安全案例synchronizedLock第三步学习返回值和任务封装CallableFutureFutureTask第四步重点掌握线程池ExecutorServiceThreadPoolExecutor七大参数执行流程拒绝策略第五步准备面试专题sleep()和wait()区别synchronized和Lock区别线程池流程死锁volatileCAS十六、开发中怎么用面试中怎么答开发中开发最重要的不是死记定义而是识别共享资源保护临界区优先考虑线程池锁粒度尽量小防止死锁合理使用并发工具类面试中面试回答不要只背书要按“概念 作用 场景 区别”说。例如问线程池时你可以这么答线程池是为了复用线程、降低线程创建销毁开销、提高响应速度、方便统一管理线程资源。Java 中常用ThreadPoolExecutor来创建线程池它有 7 个核心参数。执行流程是先创建核心线程再任务入队队列满了再创建非核心线程最大线程数也满了则执行拒绝策略。实际开发中更推荐直接使用ThreadPoolExecutor而不是Executors的快捷方式因为参数更透明、更可控。这种回答会比单纯背概念好很多。十七、总结Java 线程相关知识表面上看很碎但其实主线很清楚线程创建Thread、Runnable、Callable线程控制start()、sleep()、join()、interrupt()线程安全synchronized、Lock结果获取Future、FutureTask线程管理线程池、ThreadPoolExecutor面试重点线程安全、锁、线程池、死锁、volatile、CAS真正学会线程不是会写几个 demo而是要建立一套完整框架知道线程为什么存在知道共享资源为什么不安全知道该用什么工具保护它知道实际开发中如何高效管理线程。十八、面试速记版1. 创建线程的方式继承Thread实现Runnable实现Callable2.start()和run()区别start()启动新线程run()只是普通方法3. 线程安全三要素多线程共享资源修改操作4.synchronized锁什么实例同步方法锁this静态同步方法锁类名.classsynchronized(obj)锁指定对象5.synchronized和Lock前者简单自动释放后者灵活手动释放6.Runnable和Callable前者无返回值后者有返回值7. 线程池流程核心线程队列非核心线程拒绝策略8.Executors和ThreadPoolExecutor前者快捷后者可控你要是愿意我下一条可以继续帮你做两种增强版之一第一种把这篇改成更像 CSDN 爆款风格的排版版。第二种把这篇压缩成Java 线程面试八股速记版。