Java 多线程编程教程:从入门到精通
一、 前言
多线程是 Java 编程中一项重要的技术,它允许程序同时执行多个任务,从而提高程序效率和响应速度。本教程将带你从基础知识到高级应用,全面掌握 Java 多线程编程。
二、 基础知识
- 线程的概念: 线程是进程中的一个执行单元,它可以独立运行,拥有自己的栈空间和程序计数器。一个进程可以包含多个线程,这些线程共享进程的内存空间和资源。
- 多线程的优势:
- 提高程序效率: 多个线程可以同时执行不同的任务,充分利用CPU资源,提升程序运行速度。
- 增强用户体验: 例如,在 GUI 程序中,可以使用多个线程分别处理用户交互、数据处理等任务,避免程序阻塞,提升用户体验。
- 提高资源利用率: 多线程可以共享进程的资源,避免重复创建和销毁资源,提高资源利用率。
- 线程的生命周期:
- 新建(New): 线程被创建后,尚未启动。
- 可运行(Runnable): 线程已创建,等待 CPU 时间片分配。
- 运行(Running): 线程正在执行。
- 阻塞(Blocked): 线程处于等待状态,例如等待锁、输入输出操作等。
- 终止(Terminated): 线程执行完毕,或者因异常而结束。
三、 创建和启动线程
1. 继承 Thread 类:
public class MyThread extends Thread {
@Override
public void run() {
// 线程执行的代码
System.out.println("线程正在运行...");
}
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("线程正在运行...");
}
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start(); // 启动线程
}
}
四、 线程同步
- 同步问题: 多个线程同时访问共享资源时,可能出现数据不一致的情况,例如多个线程同时修改同一个变量。
- 解决方法: 使用同步机制,例如锁(synchronized关键字,ReentrantLock)和信号量(Semaphore)等。
1. synchronized 关键字:
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
}
public class MyThread extends Thread {
private Counter counter;
public MyThread(Counter counter) {
this.counter = counter;
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
}
public static void main(String[] args) {
Counter counter = new Counter();
MyThread thread1 = new MyThread(counter);
MyThread thread2 = new MyThread(counter);
thread1.start();
thread2.start();
// 等待两个线程执行完毕
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("count: " + counter.count);
}
}
2. ReentrantLock:
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private int count = 0;
private ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
}
// 其他代码与上面类似
五、 线程通信
- 线程之间相互协作: 线程之间需要进行通信,例如一个线程等待另一个线程完成任务,或者一个线程向另一个线程发送消息。
- 方法:
- wait() 和 notify() 方法: 用于线程之间的阻塞和唤醒。
- BlockingQueue: 用于线程之间安全地传递数据。
- Condition: 用于创建多个等待队列,实现更灵活的线程通信。
1. wait() 和 notify() 方法:
public class ProducerConsumer {
private Object lock = new Object();
private boolean hasData = false;
private String data;
public void produce(String data) {
synchronized (lock) {
while (hasData) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.data = data;
hasData = true;
lock.notify();
}
}
public String consume() {
synchronized (lock) {
while (!hasData) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
hasData = false;
lock.notify();
return data;
}
}
public static void main(String[] args) {
ProducerConsumer pc = new ProducerConsumer();
Thread producer = new Thread(() -> {
for (int i = 0; i < 10; i++) {
pc.produce("数据" + i);
}
});
Thread consumer = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("消费数据: " + pc.consume());
}
});
producer.start();
consumer.start();
}
}
2. BlockingQueue:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class ProducerConsumer {
private BlockingQueue<String> queue = new LinkedBlockingQueue<>();
public void produce(String data) {
try {
queue.put(data);
System.out.println("生产数据: " + data);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public String consume() {
try {
String data = queue.take();
System.out.println("消费数据: " + data);
return data;
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
ProducerConsumer pc = new ProducerConsumer();
Thread producer = new Thread(() -> {
for (int i = 0; i < 10; i++) {
pc.produce("数据" + i);
}
});
Thread consumer = new Thread(() -> {
for (int i = 0; i < 10; i++) {
pc.consume();
}
});
producer.start();
consumer.start();
}
}
3. Condition:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ProducerConsumer {
private ReentrantLock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
private int count = 0;
private int capacity = 10;
public void produce(String data) {
lock.lock();
try {
while (count == capacity) {
notFull.await();
}
// 生产数据
count++;
notEmpty.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public String consume() {
lock.lock();
try {
while (count == 0) {
notEmpty.await();
}
// 消费数据
count--;
notFull.signal();
return "数据";
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return null;
}
public static void main(String[] args) {
ProducerConsumer pc = new ProducerConsumer();
Thread producer = new Thread(() -> {
for (int i = 0; i < 10; i++) {
pc.produce("数据" + i);
}
});
Thread consumer = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("消费数据: " + pc.consume());
}
});
producer.start();
consumer.start();
}
}
六、 线程池
- 线程池的优势:
- 减少创建和销毁线程的开销: 线程池可以复用线程,避免频繁创建和销毁线程,提高效率。
- 控制线程数量: 可以根据系统资源限制,控制线程池的大小,避免线程过多导致资源耗尽。
- 管理线程: 线程池可以管理线程的生命周期,提供线程监控和管理功能。
1. ThreadPoolExecutor:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交任务
for (int i = 0; i < 10; i++) {
executor.execute(() -> {
System.out.println("线程正在执行任务...");
});
}
// 关闭线程池
executor.shutdown();
}
}
七、 线程安全类
- Java 提供了一些线程安全的类,例如:
- AtomicInteger: 线程安全的整数类,提供原子操作。
- AtomicLong: 线程安全的 long 类型类。
- AtomicBoolean: 线程安全的 boolean 类型类。
- ConcurrentHashMap: 线程安全的 HashMap。
- CopyOnWriteArrayList: 线程安全的 ArrayList,写操作会创建一个新的数组。
八、 高级应用
- 多线程编程的常见模式:
- 生产者-消费者模式: 一个或多个生产者线程将数据写入共享队列,一个或多个消费者线程从队列中取出数据进行处理。
- 线程池模式: 创建一个线程池,将任务提交到线程池中执行,线程池会自动管理线程的生命周期。
- 工作者线程模式: 创建多个工作者线程,将任务分发给工作者线程执行,每个工作者线程都从任务队列中获取任务并执行。
- 并行计算: 使用多线程进行并行计算,可以大幅提高计算效率。
- 异步编程: 使用异步编程,可以提高程序的响应速度,例如使用 Future 类或 Callable 接口。
九、 总结
Java 多线程编程是一项复杂但强大的技术,它可以提高程序效率、增强用户体验、提升资源利用率。本教程介绍了 Java 多线程编程的基础知识、线程同步、线程通信、线程池以及高级应用。希望这篇文章能帮助你更好地理解和应用 Java 多线程编程。
十、 练习
- 创建一个模拟银行账户的程序,使用多线程模拟多个用户同时存取款。
- 创建一个模拟餐厅点餐的程序,使用多线程模拟厨师、服务员和顾客。
- 创建一个使用线程池进行图片处理的程序,例如批量压缩或裁剪图片。
十一、 参考资料
- Java Concurrency in Practice
- Java 并发编程实战
- Java 官方文档
最后,希望你能通过学习这篇文章,对 Java 多线程编程有一个更深入的了解,并在实际开发中灵活运用。