你学 Java 学到一半,发现程序只能一件事做完再做下一件,遇到批量任务就慢得难受?一句话答案:java 多线程就是让一个程序同时跑多段代码的能力,掌握 Thread、Runnable、synchronized 和线程池,你就能把耗时任务并行起来。这篇文章,编程狮用三个能直接复制运行的例子,带你从「为什么要用」走到「怎么写才安全」,把最容易晕的线程安全问题讲明白,跟着敲一遍就能上手。你不需要先背概念,先把例子跑起来,再回头看原理会轻松很多。

一、java 多线程到底解决什么问题
先想一个生活场景:你煮面时要一直盯着锅,就没法同时去切菜,两件事只能串行做。程序也一样,单线程里下载文件时界面就卡住,因为 CPU 被这一件事占满了。java 多线程解决的正是这个问题——它让下载、界面刷新、数据处理各占一条「轨道」同时推进。
严格说,单核 CPU 也是快速切换来制造「同时」的错觉,多核才是真的并行。但对写代码的人来说,关心的不是底层怎么切,而是「我能不能把任务拆开同时跑」。想先把 Java 语法地基打牢,可以看 Java 基础教程,多线程是建立在类、对象这些基础之上的能力。
打个比方,单线程像只有一个收银台的超市,排队的人只能挨个结账;多线程就是多开几个收银台,整体 throughput 一下就上去了。代价是多个收银台会抢同一件库存,这就引出了后面要专门处理的线程安全问题。
二、java 多线程的两种写法:Thread 与 Runnable
入门最常见的是两种写法。第一种直接继承 Thread,重写它的 run 方法,再调用 start 启动:
class MyThread extends Thread {
public void run() {
System.out.println("线程 " + getName() + " 正在运行");
}
}
// 启动方式
MyThread t = new MyThread();
t.start();
第二种是实现 Runnable 接口,把它交给 Thread 去跑:
class MyTask implements Runnable {
public void run() {
System.out.println("编程狮任务在线程 " + Thread.currentThread().getName());
}
}
// 启动方式
new Thread(new MyTask()).start();
两种都能跑起来,但更推荐 Runnable。因为 Java 只支持单继承,继承了 Thread 就不能再继承别的类;而实现 Runnable 接口后,你的任务类还能自由扩展。这个细节在初学阶段容易忽略,早点养成用 Runnable 的习惯更稳妥。
还有个小陷阱:启动线程必须调用 start,而不是 run。直接调用 run 只是在当前线程里普通地执行方法,并没有开辟新线程,看起来没报错,其实根本没并发,调试时特别容易上当。
三、线程安全与 synchronized 关键字
多线程最坑的地方是「线程安全」。多个线程同时改同一个变量,就可能互相覆盖。比如两个线程各对计数器加一,理想结果是 2,实际可能只加了 1,因为「读取—计算—写回」三步被穿插执行了。
class Counter {
private int count = 0;
public synchronized void add() {
count++;
}
public int get() { return count; }
}
给方法加上 synchronized 关键字,就相当于给这段代码加了把锁:同一时间只有一个线程能进。这就保证了加法不会被打断。synchronized 是 java 多线程里最基础的同步手段,先理解它,后面学 Lock、并发集合才不会发懵。记不住这些类和方法时,Java 速查手册 可以像字典一样随时翻。
要注意,synchronized 锁的是对象实例。如果你 new 了两个不同的 Counter,它们各自一把锁,互相之间并不互斥。所以共享数据通常要写成单例或统一引用,锁才能真正生效,这是新手常忽略的一点。
四、用线程池管理一批任务
如果每来一个任务就 new 一个线程,任务一多系统就被拖垮。线程池的思路是「先养好一批线程,任务来了直接派活」,用完回收复用。Java 用 ExecutorService 来管这件事:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
ExecutorService pool = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) {
int taskId = i;
pool.submit(() -> System.out.println("执行任务 " + taskId));
}
pool.shutdown();
上面建了固定 4 条线程的池,10 个任务排队由它们轮流执行。线程池能控制并发上限、减少创建销毁的开销,是写多线程程序时的标配做法。真实项目里几乎不会手动 new 线程,而是统一交给线程池调度。
提交任务用 submit,它返回 Future,可以之后取结果或捕获异常;如果只是跑、不关心返回值,用 execute 也行。任务全提交完记得调用 shutdown,否则线程池里的线程会一直存活,程序不会正常退出。
五、三个可运行场景带你练手
把前面三点串起来,你可以试三个场景:第一,用 Runnable 起 5 个线程模拟并发计数器,观察没加锁时的错乱;第二,给计数器加 synchronized,验证结果变正确;第三,把 10 个任务丢进线程池,体会「少量线程跑大量任务」的顺畅。三个例子覆盖了 java 多线程的主干用法,敲熟它们,你就算真正入门了。
如果你本地 JDK 还没装好、例子跑不起来,可以先看 JDK 一站式安装教程,把环境配通再回来练,顺序别反了。
这三个场景由浅入深:计数器演示并发错乱,加锁演示如何修复,线程池演示工程化用法。建议每跑完一个就改改参数观察输出,比只读代码体会深得多。

总结
java 多线程的核心就四件事:用 Thread 或 Runnable 启动线程、用 synchronized 保护共享数据、用线程池管理并发、在实战里把三者串起来。你带走的重点是:优先用 Runnable、共享变量一定要考虑线程安全、真实项目用线程池而非手动建线程。下一步建议系统学一遍并发容器与 Lock 体系,再去看面试常问的线程生命周期,循序渐进最稳。
延伸学习
想把这块知识系统补齐,可以按这个顺序来:
- 先跟着 Java 入门课程 把语法与多线程基础系统过一遍,形成完整认知;
- 想深入原理与面试题,参考这篇 Java多线程与并发详解笔记,里面有更细的剖析;
- 想边学边练,Java基础:面向对象编程 OOP/javaee课程 是动手写代码的形式,适合巩固刚学到的线程写法。
常见问题
Q:Runnable 和 Thread 哪个更好?
A:优先选 Runnable。它避免了单继承的限制,任务类还能复用到线程池里;而继承 Thread 的写法更直观,适合最开始的入门演示,二者运行效果一致。
Q:synchronized 加在方法上会不会很慢?
A:对大多数业务场景,synchronized 带来的开销可以忽略。它保证的是正确性,没它在并发下数据就会错。等真遇到性能瓶颈,再考虑更细粒度的锁,不要过早优化。
Q:线程池大小设多少合适?
A:没有万能值。CPU 密集型任务线程数接近核数即可,IO 密集型可以适当调大。新手先用固定大小的小池试跑,观察任务堆积情况再调整,比拍脑袋设大数更稳。

免费 AI IDE



