Java 多线程编程
十分想念顺店杂可。。。
Java 多线程编程是 Java 并发编程的核心内容,它允许程序同时执行多个任务(线程),从而提高程序的执行效率和资源利用率。下面从基础概念、实现方式、生命周期、核心操作、同步机制、通信方式、线程池等方面详细介绍:
一、基本概念
- 进程:操作系统资源分配的基本单位,一个运行中的程序就是一个进程(如打开的浏览器)。
- 线程:进程内的执行单元,是 CPU 调度的基本单位。一个进程可以包含多个线程,线程共享进程的内存资源(如堆内存),但有自己独立的栈内存。
- 多线程的优势:
- 提高 CPU 利用率(当一个线程阻塞时,其他线程可继续执行);
- 提升程序响应速度(如 UI 程序中,后台任务不阻塞界面交互);
- 便于处理并发任务(如服务器同时处理多个客户端请求)。
二、Java 实现多线程的两种核心方式
Java 中创建线程的核心方式有两种(本质都是基于Runnable接口):
1. 继承Thread类
Thread类是 Java 中线程的基础类,它实现了Runnable接口。步骤:
- 自定义类继承
Thread; - 重写
run()方法(线程执行的核心逻辑); - 创建线程对象,调用
start()方法启动线程(而非直接调用run(),start()会触发 JVM 创建新线程并执行run())。
示例代码:
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
try {
Thread.sleep(500); // 线程休眠500ms(模拟任务耗时)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("线程1");
t2.setName("线程2");
t1.start(); // 启动线程1
t2.start(); // 启动线程2
}
}
2. 实现Runnable接口
由于 Java 是单继承(一个类只能继承一个父类),实现Runnable接口更灵活(可同时继承其他类)。步骤:
- 自定义类实现
Runnable接口; - 实现
run()方法; - 创建
Thread对象,将Runnable实例作为参数传入; - 调用
Thread对象的start()方法启动线程。
示例代码:
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(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class RunnableDemo {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread t1 = new Thread(runnable, "线程A");
Thread t2 = new Thread(runnable, "线程B");
t1.start();
t2.start();
}
}
三、线程的生命周期
Java 线程有 5 种状态,状态转换是多线程编程的核心逻辑:
- 新建(New):线程对象创建后(未调用
start())的状态。 - 就绪(Runnable):调用
start()后,线程进入就绪队列,等待 CPU 调度(此时未真正执行)。 - 运行(Running):CPU 调度线程后,执行
run()方法的状态。 - 阻塞(Blocked/Waiting/Timed Waiting):线程暂停执行,放弃 CPU 资源,直到满足条件后重新进入就绪状态。
- 阻塞原因:
sleep(long)(超时后自动唤醒)、wait()(需被notify()唤醒)、等待同步锁(synchronized竞争失败)等。
- 阻塞原因:
- 死亡(Terminated):
run()方法执行完毕,或线程被stop()(已过时,可能导致资源未释放)终止。
四、线程的核心方法
| 方法 | 作用 |
|---|---|
start() | 启动线程,将线程从 “新建” 转为 “就绪”(由 JVM 调用run())。 |
run() | 线程执行的核心逻辑(需重写)。 |
sleep(long ms) | 让当前线程休眠指定毫秒数(进入Timed Waiting状态,不释放锁)。 |
join() | 让当前线程等待调用者线程执行完毕(如t1.join():主线程等待 t1 结束)。 |
yield() | 让当前线程主动放弃 CPU 资源,重新进入就绪状态(礼让,不保证成功)。 |
interrupt() | 中断线程(设置中断标志,需配合isInterrupted()判断)。 |
isInterrupted() | 判断线程是否被中断(不清除中断标志)。 |
setDaemon(boolean) | 设置为守护线程(如垃圾回收线程),当所有非守护线程结束时自动终止。 |
五、线程同步(解决线程安全问题)
当多个线程共享资源(如全局变量)时,可能出现数据不一致(线程安全问题)。例如:两个线程同时对同一个变量做i++操作,可能导致结果错误(i++非原子操作,分 “读 - 改 - 写” 三步)。
解决方式:通过同步机制保证同一时间只有一个线程访问共享资源。
1. synchronized关键字(隐式锁)
synchronized可修饰方法或代码块,本质是通过 “对象锁” 保证同步:
- 同步方法:锁对象是
this(实例方法)或类对象(静态方法)。 - 同步代码块:锁对象可自定义(更灵活,缩小同步范围)。
示例:解决i++线程安全问题
class Counter {
private int count = 0;
// 同步方法(锁对象为this)
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class SyncDemo {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
counter.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
counter.increment();
}
});
t1.start();
t2.start();
t1.join(); // 等待t1结束
t2.join(); // 等待t2结束
System.out.println("最终结果:" + counter.getCount()); // 正确输出20000
}
}
2. Lock接口(显式锁,JDK 1.5+)
java.util.concurrent.locks.Lock是更灵活的同步机制,需手动获取和释放锁(try-finally确保释放)。常用实现类ReentrantLock(可重入锁)。
示例:用Lock解决上述问题
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Counter {
private int count = 0;
private Lock lock = new ReentrantLock(); // 创建锁对象
public void increment() {
lock.lock(); // 获取锁
try {
count++;
} finally {
lock.unlock(); // 释放锁(必须在finally中,避免锁泄漏)
}
}
public int getCount() {
return count;
}
}
Lock vs synchronized:
Lock需手动释放锁,synchronized自动释放;Lock可尝试获取锁(tryLock()),避免死锁;Lock可实现公平锁(按等待顺序获取锁),synchronized是非公平锁。
六、线程通信(线程间协作)
多个线程通过共享资源协作时,需通过通信机制协调执行顺序(如 “生产者 - 消费者” 模型)。核心方法:wait()、notify()、notifyAll()(需在synchronized块中使用)。
wait():让当前线程释放锁,进入等待队列(Waiting状态),直到被notify()唤醒。notify():随机唤醒等待队列中的一个线程。notifyAll():唤醒等待队列中的所有线程。
示例:生产者 - 消费者模型(生产一个,消费一个)
class SharedResource {
private int data;
private boolean hasData = false; // 标记是否有数据可供消费
// 生产数据
public synchronized void produce(int newData) throws InterruptedException {
while (hasData) { // 若已有数据,生产者等待
wait();
}
data = newData;
hasData = true;
System.out.println("生产:" + data);
notify(); // 唤醒消费者
}
// 消费数据
public synchronized int consume() throws InterruptedException {
while (!hasData) { // 若无数据,消费者等待
wait();
}
hasData = false;
System.out.println("消费:" + data);
notify(); // 唤醒生产者
return data;
}
}
public class ProducerConsumer {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
// 生产者线程
Thread producer = new Thread(() -> {
for (int i = 1; i <= 5; i++) {
try {
resource.produce(i);
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 消费者线程
Thread consumer = new Thread(() -> {
for (int i = 1; i <= 5; i++) {
try {
resource.consume();
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
producer.start();
consumer.start();
}
}
七、线程池(Executor框架)
频繁创建 / 销毁线程会消耗大量资源,线程池可复用线程,降低开销。JDK 通过java.util.concurrent.Executor框架提供线程池支持,核心类ThreadPoolExecutor。
1. 常用线程池类型(通过Executors工具类创建)
| 线程池类型 | 特点 | 适用场景 |
|---|---|---|
FixedThreadPool | 固定大小的线程池,核心线程数 = 最大线程数。 | 任务数量固定的场景 |
CachedThreadPool | 可缓存的线程池,无核心线程,最大线程数不限(按需创建,超时回收)。 | 短期、轻量任务 |
ScheduledThreadPool | 支持定时 / 周期性任务的线程池。 | 定时任务(如定时备份) |
SingleThreadExecutor | 单线程池,所有任务按顺序执行。 | 需要顺序执行的任务 |
2. 线程池使用示例
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo {
public static void main(String[] args) {
// 创建固定大小为3的线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
// 提交5个任务
for (int i = 0; i < 5; i++) {
int taskId = i;
executor.submit(() -> { // 提交任务(Runnable)
System.out.println("任务" + taskId + "由线程" + Thread.currentThread().getName() + "执行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown(); // 关闭线程池(不再接受新任务,等待已有任务完成)
}
}
八、高级主题
- 线程安全集合:
java.util.concurrent包下的集合(如ConcurrentHashMap、CopyOnWriteArrayList),内部实现了线程安全,无需手动同步。 ThreadLocal:为每个线程提供独立的变量副本,避免线程安全问题(如SimpleDateFormat非线程安全,可用ThreadLocal包装)。- 原子类:
java.util.concurrent.atomic包下的类(如AtomicInteger),通过 CAS(Compare-And-Swap)机制实现无锁线程安全操作。 - 死锁:多个线程互相等待对方释放锁导致的无限阻塞。避免方式:按固定顺序获取锁、设置超时时间(
tryLock())等。
总结
Java 多线程编程的核心是线程创建、状态管理、同步机制、线程通信和线程池。合理使用多线程可提升程序性能,但需注意线程安全问题(通过synchronized或Lock解决),并避免死锁、资源泄漏等问题。实际开发中,优先使用线程池而非手动创建线程,以提高资源利用率。
更多推荐


所有评论(0)