Java线程通信
外观
Java线程通信[编辑 | 编辑源代码]
Java线程通信是多线程编程中的核心概念,指多个线程之间通过共享内存或消息传递机制协调工作。合理使用线程通信可以避免竞态条件、死锁等问题,并提高程序的并发效率。
概述[编辑 | 编辑源代码]
在Java中,线程通信主要通过以下方式实现:
- 共享对象:通过对象的成员变量或静态变量共享数据
- wait/notify机制:使用Object类的wait(), notify(), notifyAll()方法
- 并发工具类:如BlockingQueue、CountDownLatch等
线程通信需要解决两个关键问题: 1. 线程如何安全地共享信息 2. 如何确保线程在正确的时间执行
基本通信机制[编辑 | 编辑源代码]
wait/notify机制[编辑 | 编辑源代码]
这是Java内建的线程通信方式,所有对象都拥有这些方法(因为定义在Object类中)。
public class WaitNotifyExample {
private static final Object lock = new Object();
private static boolean condition = false;
public static void main(String[] args) {
Thread waitingThread = new Thread(() -> {
synchronized (lock) {
while (!condition) {
try {
System.out.println("等待线程进入等待状态");
lock.wait(); // 释放锁并等待
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
System.out.println("等待线程被唤醒并继续执行");
}
});
Thread notifyingThread = new Thread(() -> {
synchronized (lock) {
try {
Thread.sleep(2000); // 模拟工作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
condition = true;
lock.notify(); // 唤醒等待的线程
System.out.println("通知线程发送了通知");
}
});
waitingThread.start();
notifyingThread.start();
}
}
输出示例:
等待线程进入等待状态 (2秒后) 通知线程发送了通知 等待线程被唤醒并继续执行
使用BlockingQueue[编辑 | 编辑源代码]
Java并发包提供了线程安全的队列实现:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueExample {
public static void main(String[] args) {
BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
// 生产者线程
new Thread(() -> {
try {
String item = "数据-" + System.currentTimeMillis();
queue.put(item); // 如果队列满则阻塞
System.out.println("生产: " + item);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
// 消费者线程
new Thread(() -> {
try {
String item = queue.take(); // 如果队列空则阻塞
System.out.println("消费: " + item);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
}
高级通信模式[编辑 | 编辑源代码]
生产者-消费者模式[编辑 | 编辑源代码]
经典的多线程通信模型,可以使用多种方式实现:
信号量模式[编辑 | 编辑源代码]
使用Semaphore控制资源访问:
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
private static final Semaphore semaphore = new Semaphore(1); // 只允许1个线程访问
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 获得许可");
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
semaphore.release();
System.out.println(Thread.currentThread().getName() + " 释放许可");
}
}).start();
}
}
}
实际应用案例[编辑 | 编辑源代码]
银行转账系统[编辑 | 编辑源代码]
考虑一个银行系统,多个线程同时处理转账请求,需要确保账户余额的正确性:
public class BankTransfer {
private static class Account {
private int balance;
public Account(int initialBalance) {
this.balance = initialBalance;
}
public synchronized void transfer(Account dest, int amount) {
if (this.balance >= amount) {
this.balance -= amount;
dest.balance += amount;
System.out.println(Thread.currentThread().getName() +
" 转账成功: " + amount);
} else {
System.out.println(Thread.currentThread().getName() +
" 余额不足");
}
}
}
public static void main(String[] args) {
Account accountA = new Account(1000);
Account accountB = new Account(1000);
// 创建多个转账线程
for (int i = 0; i < 5; i++) {
new Thread(() -> {
for (int j = 0; j < 10; j++) {
accountA.transfer(accountB, 50);
accountB.transfer(accountA, 30);
}
}).start();
}
}
}
常见问题与解决方案[编辑 | 编辑源代码]
问题 | 解决方案 |
---|---|
死锁 | 使用锁顺序、设置超时、避免嵌套锁 |
活锁 | 引入随机退避机制 |
线程饥饿 | 使用公平锁、合理设置线程优先级 |
数学基础[编辑 | 编辑源代码]
线程同步问题可以用Petri网建模。对于n个线程访问共享资源的情况,安全条件为:
其中:
- 是线程i的资源占用数
- 是资源总量
最佳实践[编辑 | 编辑源代码]
1. 优先使用高层并发工具(如java.util.concurrent包) 2. 尽量减少同步代码块的范围 3. 避免在同步块中调用外部方法 4. 考虑使用不可变对象 5. 使用线程局部变量(ThreadLocal)避免共享
总结[编辑 | 编辑源代码]
Java线程通信是多线程编程的核心,理解各种通信机制及其适用场景对于编写正确、高效的并发程序至关重要。从基本的wait/notify到高级的并发工具类,Java提供了丰富的线程通信方式。开发者应根据具体需求选择最合适的通信机制,同时注意避免常见的并发问题。