Java 多线程怎么学:三个实战场景带你轻松上手

编程狮(w3cschool.cn) 2026-09-04 18:39:08 浏览数 (48)
反馈

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

Java 多线程怎么学:三个实战场景带你轻松上手封面图

一、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 起线程三种方式分别适用什么场景

总结

java 多线程的核心就四件事:用 Thread 或 Runnable 启动线程、用 synchronized 保护共享数据、用线程池管理并发、在实战里把三者串起来。你带走的重点是:优先用 Runnable、共享变量一定要考虑线程安全、真实项目用线程池而非手动建线程。下一步建议系统学一遍并发容器与 Lock 体系,再去看面试常问的线程生命周期,循序渐进最稳。

延伸学习

想把这块知识系统补齐,可以按这个顺序来:

  1. 先跟着 Java 入门课程 把语法与多线程基础系统过一遍,形成完整认知;
  2. 想深入原理与面试题,参考这篇 Java多线程与并发详解笔记,里面有更细的剖析;
  3. 想边学边练,Java基础:面向对象编程 OOP/javaee课程 是动手写代码的形式,适合巩固刚学到的线程写法。

常见问题

Q:Runnable 和 Thread 哪个更好?

A:优先选 Runnable。它避免了单继承的限制,任务类还能复用到线程池里;而继承 Thread 的写法更直观,适合最开始的入门演示,二者运行效果一致。

Q:synchronized 加在方法上会不会很慢?

A:对大多数业务场景,synchronized 带来的开销可以忽略。它保证的是正确性,没它在并发下数据就会错。等真遇到性能瓶颈,再考虑更细粒度的锁,不要过早优化。

Q:线程池大小设多少合适?

A:没有万能值。CPU 密集型任务线程数接近核数即可,IO 密集型可以适当调大。新手先用固定大小的小池试跑,观察任务堆积情况再调整,比拍脑袋设大数更稳。

0 人点赞