Java 多线程编程
Java 多线程编程是 Java 平台的核心特性之一,用于实现并发执行,提高程序的性能和响应能力。它广泛应用于服务器开发、GUI 应用、游戏开发等场景。以下是一个简明实用的 Java 多线程编程入门教程,涵盖核心概念、实现方式、线程安全、常见工具和示例代码,适合初学者快速上手。
1. 多线程简介
- 定义:多线程是指在同一程序中同时运行多个线程(Thread),每个线程执行不同的任务。Java 提供了内置支持,通过
java.lang.Thread
类和java.util.concurrent
包实现。 - 优势:
- 提高 CPU 利用率(并行执行任务)。
- 提升程序响应速度(如 GUI 不卡顿)。
- 简化异步任务处理。
- 挑战:
- 线程安全:多线程访问共享资源可能导致数据不一致。
- 线程同步:需要协调线程以避免竞争条件。
- 性能开销:线程创建和切换有一定成本。
2. 创建线程的两种方式
Java 提供了两种主要方式创建线程:
2.1 继承 Thread 类
- 定义一个类继承
Thread
,重写run()
方法。 - 调用
start()
方法启动线程。
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread running: " + Thread.currentThread().getName());
}
}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // 启动线程
}
}
- 说明:
start()
调用后,JVM 创建新线程并执行run()
方法。直接调用run()
不会创建新线程。
2.2 实现 Runnable 接口
- 定义一个类实现
Runnable
接口,实现run()
方法。 - 将
Runnable
对象传递给Thread
构造器。
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Thread running: " + Thread.currentThread().getName());
}
}
public class Main {
public static void main(String[] args) {
Runnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
}
- 优点:
Runnable
更灵活,支持接口实现,可继承其他类。
推荐:优先使用 Runnable
,因为它更符合面向接口编程,且便于与线程池等高级工具结合。
3. 线程的基本操作
3.1 线程状态
线程有以下状态(Thread.State
枚举):
- NEW:创建但未启动。
- RUNNABLE:正在运行或等待 CPU。
- BLOCKED:等待锁(同步块)。
- WAITING:等待其他线程通知(如
wait()
、join()
)。 - TIMED_WAITING:有限时间等待(如
sleep()
、wait(timeout)
)。 - TERMINATED:线程执行完成。
3.2 常用方法
start()
:启动线程。run()
:定义线程执行的任务。sleep(long millis)
:让线程休眠指定毫秒,暂停执行。join()
:等待线程执行完成。setPriority(int priority)
:设置线程优先级(1-10,默认为 5)。interrupt()
:中断线程(需配合处理中断状态)。Thread.currentThread()
:获取当前线程。
示例:休眠和优先级
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 3; i++) {
System.out.println("T1: " + i);
try {
Thread.sleep(1000); // 休眠 1 秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.setPriority(Thread.MAX_PRIORITY); // 设置最高优先级
t1.start();
}
}
4. 线程同步与线程安全
多线程访问共享资源可能导致数据不一致,因此需要同步机制。
4.1 synchronized 关键字
- 作用:确保同一时间只有一个线程访问同步代码块或方法。
- 方式:
- 同步方法:在方法上加
synchronized
。 - 同步块:使用
synchronized(obj)
指定锁对象。
示例:计数器同步
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Count: " + counter.getCount()); // 输出 2000
}
}
- 说明:
synchronized
确保increment()
方法线程安全。
4.2 Lock 接口
Java 提供了 java.util.concurrent.locks.Lock
(如 ReentrantLock
),比 synchronized
更灵活,支持超时和可中断锁。
示例:使用 ReentrantLock
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Counter {
private int count = 0;
private final Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
return count;
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Count: " + counter.getCount()); // 输出 2000
}
}
5. 线程通信
线程之间需要协作,常用机制包括:
wait()
和notify()
/notifyAll()
:- 在
synchronized
块中使用,线程等待或唤醒。 Condition
:与Lock
结合使用,类似wait/notify
。
示例:生产者-消费者
import java.util.LinkedList;
import java.util.Queue;
class ProducerConsumer {
private Queue<Integer> queue = new LinkedList<>();
private final int MAX_SIZE = 5;
public synchronized void produce(int value) throws InterruptedException {
while (queue.size() == MAX_SIZE) {
wait(); // 队列满,等待
}
queue.add(value);
System.out.println("Produced: " + value);
notifyAll(); // 唤醒消费者
}
public synchronized int consume() throws InterruptedException {
while (queue.isEmpty()) {
wait(); // 队列空,等待
}
int value = queue.poll();
System.out.println("Consumed: " + value);
notifyAll(); // 唤醒生产者
return value;
}
}
public class Main {
public static void main(String[] args) {
ProducerConsumer pc = new ProducerConsumer();
Thread producer = new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
pc.produce(i);
Thread.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread consumer = new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
pc.consume();
Thread.sleep(150);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
producer.start();
consumer.start();
}
}
- 输出示例:
Produced: 1
Consumed: 1
Produced: 2
Consumed: 2
...
6. 线程池
创建大量线程会消耗资源,Java 提供了 java.util.concurrent.ExecutorService
来管理线程池。
6.1 创建线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2); // 固定 2 个线程
for (int i = 1; i <= 5; i++) {
int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId + " running in " + Thread.currentThread().getName());
});
}
executor.shutdown(); // 关闭线程池
}
}
- 说明:
Executors.newFixedThreadPool(n)
:创建固定大小的线程池。submit()
:提交任务(支持Runnable
或Callable
)。shutdown()
:等待任务完成后关闭线程池。
6.2 常见线程池
Executors.newFixedThreadPool(int n)
:固定线程数。Executors.newCachedThreadPool()
:动态调整线程数。Executors.newSingleThreadExecutor()
:单线程执行。Executors.newScheduledThreadPool(int n)
:支持定时任务。
7. 并发工具(java.util.concurrent)
Java 提供了丰富的并发工具:
- CountDownLatch:等待多个线程完成。
import java.util.concurrent.CountDownLatch;
public class Main {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
Runnable task = () -> {
System.out.println(Thread.currentThread().getName() + " finished");
latch.countDown();
};
new Thread(task).start();
new Thread(task).start();
new Thread(task).start();
latch.await(); // 等待 3 个线程完成
System.out.println("All tasks done");
}
}
- CyclicBarrier:多个线程等待彼此到达某点。
- Semaphore:控制同时访问资源的线程数。
- ConcurrentHashMap:线程安全的哈希表。
- BlockingQueue:线程安全的队列(如
ArrayBlockingQueue
)。
8. 注意事项
- 线程安全:共享资源需使用
synchronized
、锁或并发集合。 - 死锁:避免多个线程互相等待锁(使用锁排序或超时机制)。
- 性能:线程池优于频繁创建线程,减少上下文切换。
- 中断处理:捕获
InterruptedException
,正确处理线程中断。 - 现代替代:Java 8+ 的
CompletableFuture
和parallelStream
可简化异步编程。
9. 应用场景
- Web 服务器:处理多个客户端请求(如 Tomcat)。
- 任务调度:定时任务或后台处理。
- GUI 应用:异步更新界面,避免卡顿。
- 数据处理:并行处理大数据(如文件解析、图像处理)。
10. 总结
- 核心概念:线程创建(
Thread
、Runnable
)、同步(synchronized
、Lock
)、线程池(ExecutorService
)、并发工具。 - 推荐实践:
- 使用
Runnable
或Callable
而非继承Thread
。 - 优先使用线程池管理线程。
- 确保线程安全,合理选择同步机制。
- 学习资源:
- 《Java Concurrency in Practice》(经典书籍)。
- Oracle Java 并发教程(https://docs.oracle.com/javase/tutorial/essential/concurrency/)。
- 实践:实现生产者-消费者、线程池任务调度等。
如果你需要更深入的某部分(如线程池优化、死锁调试、CompletableFuture 使用)或具体场景的代码示例,请告诉我!