Java 多线程基础入门:线程创建与常用方法详解
【摘要】 前言多线程是 Java 开发中的核心知识点,也是面试中的高频考点。掌握多线程编程,能让我们写出性能更高、响应更快的程序。本文从最基础的线程创建讲起,带你快速入门 Java 多线程。 一、什么是线程和进程?进程:操作系统中正在运行的程序,是资源分配的基本单位。比如你打开一个浏览器就是一个进程。线程:进程中的一个执行单元,是 CPU 调度的基本单位。一个进程可以包含多个线程,它们共享进程的资源...
前言
多线程是 Java 开发中的核心知识点,也是面试中的高频考点。掌握多线程编程,能让我们写出性能更高、响应更快的程序。本文从最基础的线程创建讲起,带你快速入门 Java 多线程。
一、什么是线程和进程?
- 进程:操作系统中正在运行的程序,是资源分配的基本单位。比如你打开一个浏览器就是一个进程。
- 线程:进程中的一个执行单元,是 CPU 调度的基本单位。一个进程可以包含多个线程,它们共享进程的资源。
简单理解:进程是"车间",线程是车间里的"工人"。
二、创建线程的三种方式
1. 继承 Thread 类
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程执行:" + Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
2. 实现 Runnable 接口(推荐)
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程执行:" + Thread.currentThread().getName());
}
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start();
}
}
3. 实现 Callable 接口(有返回值)
import java.util.concurrent.*;
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "线程返回结果";
}
public static void main(String[] args) throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(1);
Future<String> future = pool.submit(new MyCallable());
System.out.println(future.get());
pool.shutdown();
}
}
三种方式对比
| 方式 | 优点 | 缺点 |
|---|---|---|
| 继承 Thread | 简单直接 | Java 单继承,无法再继承其他类 |
| 实现 Runnable | 灵活,可继承其他类 | 无返回值 |
| 实现 Callable | 有返回值,可抛异常 | 使用较复杂 |
实际开发中推荐使用 Runnable 或 Callable,因为 Java 是单继承,实现接口更灵活。
三、线程的常用方法
| 方法 | 说明 |
|---|---|
start() |
启动线程,JVM 会调用 run() 方法 |
run() |
线程执行的任务逻辑 |
sleep(ms) |
让当前线程休眠指定毫秒 |
join() |
等待该线程执行完毕 |
yield() |
让出 CPU 时间片 |
interrupt() |
中断线程 |
isAlive() |
判断线程是否存活 |
getName() |
获取线程名称 |
setDaemon(true) |
设置为守护线程 |
代码示例
public class ThreadMethodDemo {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "子线程");
t.start();
t.join(); // 等待子线程执行完
System.out.println("主线程结束");
}
}
四、线程的生命周期
Java 线程有 6 种状态,定义在 Thread.State 枚举中:
- NEW:新建状态,线程对象已创建但还没调用 start()
- RUNNABLE:可运行状态,包括就绪和运行中
- BLOCKED:阻塞状态,等待获取锁
- WAITING:等待状态,无限期等待其他线程通知
- TIMED_WAITING:超时等待状态,如调用了 sleep() 或 wait(timeout)
- TERMINATED:终止状态,线程执行完毕
NEW → RUNNABLE → BLOCKED/WAITING/TIMED_WAITING → TERMINATED
五、线程安全问题
当多个线程同时操作共享资源时,可能出现数据不一致的问题。
问题示例
public class UnsafeDemo {
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
for (int i = 0; i < 10000; i++) {
count++;
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("count = " + count); // 结果通常小于 20000
}
}
解决方案:synchronized
public class SafeDemo {
private static int count = 0;
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
for (int i = 0; i < 10000; i++) {
synchronized (lock) {
count++;
}
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("count = " + count); // 结果一定是 20000
}
}
六、线程池
频繁创建和销毁线程开销很大,线程池可以复用线程,提高性能。
常用线程池
| 线程池 | 说明 |
|---|---|
newFixedThreadPool |
固定大小线程池 |
newCachedThreadPool |
可缓存线程池,按需创建 |
newSingleThreadExecutor |
单线程池 |
newScheduledThreadPool |
定时任务线程池 |
代码示例
import java.util.concurrent.*;
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int taskId = i;
pool.submit(() -> {
System.out.println("任务" + taskId + "由 " + Thread.currentThread().getName() + " 执行");
});
}
pool.shutdown();
}
}
注意:实际开发中建议使用 ThreadPoolExecutor 手动创建线程池,避免使用 Executors 工厂方法,因为它们的默认参数可能导致 OOM 等问题。
七、常见面试题
- start() 和 run() 的区别?
start() 会启动新线程并执行 run();直接调用 run() 只是普通方法调用,不会创建新线程。 - sleep() 和 wait() 的区别?
sleep() 是 Thread 的方法,不释放锁;wait() 是 Object 的方法,会释放锁,需要配合 notify()/notifyAll() 使用。 - 如何保证线程安全?
synchronized、ReentrantLock、原子类(AtomicInteger 等)、ThreadLocal、ConcurrentHashMap 等。 - volatile 关键字的作用?
保证可见性和有序性,但不保证原子性。
总结
多线程是 Java 进阶的必经之路。本文介绍了线程创建、常用方法、生命周期、线程安全和线程池等基础知识。建议多动手实践,理解每个 API 的使用场景,为后续学习并发包(JUC)打好基础。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)