一、先搞懂:什么是 Java 多线程?
可以把进程想象成一个正在运行的应用程序(比如你的微信),而线程是进程里的最小执行单元(比如微信同时处理接收消息、显示界面、播放语音)。Java 多线程就是让一个 Java 程序同时执行多个任务,充分利用 CPU 资源,提升程序运行效率(比如下载文件时,一个线程下载、一个线程显示进度、一个线程校验文件)。
二、Java 多线程的 3 种核心实现方式
方式 1:继承 Thread 类(最基础)
Thread 类是 Java 封装好的线程类,继承它并重写run()方法(线程要执行的任务),调用start()启动线程(注意:不能直接调用run(),否则只是普通方法调用)。
java
运行
// 步骤1:继承Thread类 class MyThread extends Thread { // 步骤2:重写run()方法,定义线程任务 @Override public void run() { for (int i = 0; i < 5; i++) { // Thread.currentThread().getName()获取当前线程名称 System.out.println(Thread.currentThread().getName() + " 执行:" + i); try { Thread.sleep(100); // 线程休眠100ms,模拟任务耗时 } catch (InterruptedException e) { e.printStackTrace(); } } } } public class ThreadDemo { public static void main(String[] args) { // 步骤3:创建线程对象并启动 MyThread thread1 = new MyThread(); thread1.setName("线程1"); thread1.start(); // 启动线程1 MyThread thread2 = new MyThread(); thread2.setName("线程2"); thread2.start(); // 启动线程2 } }输出效果(线程 1 和线程 2 交替执行,顺序不固定,因为 CPU 调度是随机的):
plaintext
线程1 执行:0 线程2 执行:0 线程1 执行:1 线程2 执行:1 ...方式 2:实现 Runnable 接口(推荐,避免单继承限制)
Java 是单继承,所以更推荐实现Runnable接口(只定义run()方法),再把 Runnable 对象传给 Thread 类启动。
java
运行
// 步骤1:实现Runnable接口 class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " 执行:" + i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class RunnableDemo { public static void main(String[] args) { // 步骤2:创建Runnable任务对象 MyRunnable task = new MyRunnable(); // 步骤3:将任务传给Thread并启动 Thread thread1 = new Thread(task, "线程A"); thread1.start(); Thread thread2 = new Thread(task, "线程B"); thread2.start(); } }方式 3:实现 Callable 接口(带返回值 + 可抛异常)
前两种方式没有返回值,Callable接口支持线程执行完返回结果,搭配FutureTask获取结果。
java
运行
import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; // 步骤1:实现Callable接口,指定返回值类型(这里是Integer) class MyCallable implements Callable<Integer> { @Override public Integer call() throws Exception { int sum = 0; for (int i = 1; i <= 100; i++) { sum += i; } return sum; // 线程执行结果 } } public class CallableDemo { public static void main(String[] args) throws Exception { // 步骤2:创建Callable任务 MyCallable callable = new MyCallable(); FutureTask<Integer> futureTask = new FutureTask<>(callable); // 步骤3:启动线程 Thread thread = new Thread(futureTask); thread.start(); // 步骤4:获取线程执行结果(get()会阻塞,直到线程执行完成) Integer result = futureTask.get(); System.out.println("线程执行结果:1-100求和 = " + result); // 输出5050 } }三、多线程核心问题:线程安全
多个线程同时操作共享数据时,会出现数据错乱(比如两个线程同时给一个变量加 1,结果可能不是预期值)。
解决方法:加锁(synchronized)
java
运行
// 共享资源类 class Counter { private int count = 0; // 加synchronized关键字,保证同一时间只有一个线程执行该方法 public synchronized void increment() { count++; } public int getCount() { return count; } } // 线程任务:调用increment() class CounterTask implements Runnable { private Counter counter; public CounterTask(Counter counter) { this.counter = counter; } @Override public void run() { for (int i = 0; i < 1000; i++) { counter.increment(); } } } public class ThreadSafeDemo { public static void main(String[] args) throws InterruptedException { Counter counter = new Counter(); // 两个线程操作同一个Counter对象 Thread t1 = new Thread(new CounterTask(counter)); Thread t2 = new Thread(new CounterTask(counter)); t1.start(); t2.start(); // 等待两个线程执行完成 t1.join(); t2.join(); System.out.println("最终计数:" + counter.getCount()); // 正确输出2000(不加锁可能小于2000) } }四、Java 多线程常用工具(JUC 包,新手入门核心)
Java 提供了java.util.concurrent(JUC)包,简化多线程开发,核心工具:
- 线程池(ExecutorService):避免频繁创建 / 销毁线程的开销,复用线程执行任务
java
运行
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolDemo { public static void main(String[] args) { // 创建固定大小的线程池(2个线程) ExecutorService pool = Executors.newFixedThreadPool(2); // 提交3个任务,线程池会复用2个线程执行 pool.submit(() -> System.out.println(Thread.currentThread().getName() + " 执行任务1")); pool.submit(() -> System.out.println(Thread.currentThread().getName() + " 执行任务2")); pool.submit(() -> System.out.println(Thread.currentThread().getName() + " 执行任务3")); pool.shutdown(); // 关闭线程池 } } - Lock 锁:比
synchronized更灵活(支持手动加锁 / 解锁、可中断、超时等待) - CountDownLatch:等待多个线程执行完成后,主线程再继续(比如等待所有下载线程完成,再合并文件)
五、多线程常见坑点 & 注意事项
- 线程启动错误:调用
run()而非start(),导致没有创建新线程,只是主线程执行方法。 - 线程安全问题:未加锁操作共享数据,导致数据不一致(比如电商库存超卖)。
- 线程死锁:两个线程互相持有对方需要的锁,导致都无法执行(比如线程 1 拿锁 A 等锁 B,线程 2 拿锁 B 等锁 A)。
- 过度创建线程:线程数超过 CPU 核心数太多,会导致线程切换开销过大,反而降低效率。
六、实战场景:什么时候用多线程?
- 耗时操作(下载、文件读写、网络请求):单独开线程,避免主线程阻塞(比如 GUI 程序卡界面)。
- 批量处理任务(批量导入数据、批量生成报表):用线程池并行处理,提升效率。
- 实时性要求高的场景(秒杀系统、实时监控):多线程同时处理请求。
总结
- Java 多线程核心是让程序并行执行多个任务,核心实现方式有继承 Thread、实现 Runnable(推荐)、实现 Callable(带返回值),启动线程必须调用
start()。 - 多线程操作共享数据时,必须通过
synchronized或 Lock 保证线程安全,避免数据错乱。 - 实际开发中优先使用 JUC 包的线程池(复用线程)、
CountDownLatch等工具,减少手动管理线程的复杂度,规避死锁、过度创建线程等问题。