多线程编程简介
在现代计算环境中,多线程编程是提高应用程序性能和响应能力的关键技术。Java从最初版本就提供了多线程支持,并在后续版本中不断增强了并发编程能力。
多线程允许程序同时执行多个任务,充分利用多核处理器的计算能力。然而,多线程编程也带来了复杂性,如线程安全、死锁和资源竞争等问题,需要开发者特别注意。
线程创建与管理
Java提供了两种创建线程的方式:继承Thread类和实现Runnable接口。
继承Thread类
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程运行中: " + Thread.currentThread().getName());
}
}
// 使用方式
MyThread thread = new MyThread();
thread.start();
实现Runnable接口
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程运行中: " + Thread.currentThread().getName());
}
}
// 使用方式
Thread thread = new Thread(new MyRunnable());
thread.start();
使用Lambda表达式(Java 8+)
Thread thread = new Thread(() -> {
System.out.println("使用Lambda创建的线程: " + Thread.currentThread().getName());
});
thread.start();
线程基本控制
Thread thread = new Thread(() -> {
// 线程任务
});
thread.start(); // 启动线程
thread.join(); // 等待线程结束
thread.interrupt(); // 中断线程
// 获取当前线程
Thread currentThread = Thread.currentThread();
System.out.println("当前线程: " + currentThread.getName());
线程同步机制
当多个线程访问共享资源时,需要同步机制来确保数据一致性。
synchronized关键字
class Counter {
private int count = 0;
// 同步方法
public synchronized void increment() {
count++;
}
// 同步代码块
public void decrement() {
synchronized(this) {
count--;
}
}
public synchronized int getCount() {
return count;
}
}
ReentrantLock
import java.util.concurrent.locks.ReentrantLock;
class SafeCounter {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}
volatile关键字
class VolatileExample {
private volatile boolean flag = false;
public void setFlagTrue() {
flag = true;
}
public boolean isFlag() {
return flag;
}
}
线程池与执行器框架
Java的Executor框架提供了线程池管理,避免了频繁创建和销毁线程的开销。
创建线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 创建固定大小的线程池
ExecutorService fixedPool = Executors.newFixedThreadPool(5);
// 创建缓存线程池
ExecutorService cachedPool = Executors.newCachedThreadPool();
// 创建单线程线程池
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
使用线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
// 提交Runnable任务
executor.submit(() -> {
System.out.println("任务执行中: " + Thread.currentThread().getName());
});
// 提交Callable任务并获取Future
Future future = executor.submit(() -> {
Thread.sleep(1000);
return 42;
});
// 获取任务结果
try {
Integer result = future.get();
System.out.println("任务结果: " + result);
} catch (Exception e) {
e.printStackTrace();
}
// 关闭线程池
executor.shutdown();
并发集合类
Java提供了多种线程安全的集合类,替代需要手动同步的传统集合。
ConcurrentHashMap
import java.util.concurrent.ConcurrentHashMap;
ConcurrentHashMap map = new ConcurrentHashMap();
// 线程安全的操作
map.put("key1", 1);
map.putIfAbsent("key1", 2); // 只有key不存在时才会放入
// 遍历操作也是线程安全的
map.forEach((key, value) -> {
System.out.println(key + ": " + value);
});
CopyOnWriteArrayList
import java.util.concurrent.CopyOnWriteArrayList;
CopyOnWriteArrayList list = new CopyOnWriteArrayList();
// 添加元素
list.add("item1");
list.add("item2");
// 迭代操作是线程安全的,基于快照机制
for (String item : list) {
System.out.println(item);
}
BlockingQueue
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
BlockingQueue queue = new ArrayBlockingQueue(10);
// 生产者线程
new Thread(() -> {
try {
queue.put("message");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
// 消费者线程
new Thread(() -> {
try {
String message = queue.take();
System.out.println("收到消息: " + message);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
高级并发工具
Java并发包提供了多种高级工具来处理复杂并发场景。
CountDownLatch
import java.util.concurrent.CountDownLatch;
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i {
System.out.println("子线程执行任务");
latch.countDown(); // 计数减1
}).start();
}
latch.await(); // 等待所有子线程完成
System.out.println("所有子线程已完成,主线程继续执行");
CyclicBarrier
import java.util.concurrent.CyclicBarrier;
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("所有线程已到达屏障点");
});
for (int i = 0; i {
try {
System.out.println("线程到达屏障点");
barrier.await(); // 等待其他线程
System.out.println("线程继续执行");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
Semaphore
import java.util.concurrent.Semaphore;
Semaphore semaphore = new Semaphore(3); // 允许3个线程同时访问
for (int i = 0; i {
try {
semaphore.acquire(); // 获取许可
System.out.println("线程获得许可,执行任务");
Thread.sleep(1000);
semaphore.release(); // 释放许可
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
实战案例:生产者-消费者问题
下面通过一个完整的生产者-消费者示例展示多线程编程的实际应用:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class ProducerConsumerExample {
public static void main(String[] args) {
// 创建大小为5的阻塞队列
BlockingQueue queue = new ArrayBlockingQueue(5);
// 创建生产者
Thread producer = new Thread(() -> {
try {
for (int i = 0; i {
try {
for (int i = 0; i < 10; i++) {
Integer value = queue.take(); // 如果队列空,则阻塞
System.out.println("消费者处理: " + value);
Thread.sleep(1000); // 模拟处理时间
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 启动生产者和消费者
producer.start();
consumer.start();
// 等待线程结束
try {
producer.join();
consumer.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("生产消费任务完成");
}
}
使用线程池优化
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ArrayBlockingQueue;
public class AdvancedProducerConsumer {
public static void main(String[] args) {
BlockingQueue queue = new ArrayBlockingQueue(5);
ExecutorService executor = Executors.newFixedThreadPool(4);
// 创建多个生产者
for (int i = 0; i {
try {
for (int j = 0; j < 5; j++) {
int value = (int) (Math.random() * 100);
queue.put(value);
System.out.println(Thread.currentThread().getName() +
" 生产: " + value);
Thread.sleep(300);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 创建多个消费者
for (int i = 0; i {
try {
for (int j = 0; j < 5; j++) {
Integer value = queue.take();
System.out.println(Thread.currentThread().getName() +
" 消费: " + value);
Thread.sleep(500);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
executor.shutdown();
while (!executor.isTerminated()) {
// 等待所有任务完成
}
System.out.println("所有生产消费任务完成");
}
}
最佳实践与常见陷阱
多线程编程需要遵循一些最佳实践来避免常见问题:
1. 避免死锁
// 错误的做法 - 可能导致死锁
public void transfer(Account from, Account to, int amount) {
synchronized(from) {
synchronized(to) {
from.withdraw(amount);
to.deposit(amount);
}
}
}
// 正确的做法 - 使用固定的锁顺序
public void safeTransfer(Account from, Account to, int amount) {
Object firstLock = from.getId() < to.getId() ? from : to;
Object secondLock = from.getId() < to.getId() ? to : from;
synchronized(firstLock) {
synchronized(secondLock) {
from.withdraw(amount);
to.deposit(amount);
}
}
}
2. 使用线程局部变量
// 使用ThreadLocal维护线程局部变量
private static final ThreadLocal dateFormat =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
public String formatDate(Date date) {
return dateFormat.get().format(date); // 每个线程有自己的实例
}
3. 正确处理线程中断
public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {
// 执行任务
Thread.sleep(1000);
}
} catch (InterruptedException e) {
// 恢复中断状态
Thread.currentThread().interrupt();
}
}
4. 避免过度同步
只在必要的时候同步,减少同步块的大小,提高并发性能。
5. 使用并发工具类
优先使用Java并发包中的工具类,而不是自己实现同步机制。