Java多线程模拟机场安检:线程安全问题与同步解决
本文利用Java代码模拟机场安检流程,揭示了多线程并发访问共享资源时的数据竞争风险。通过分析航班安检中身份信息错位的异常现象,明确了非原子操作导致的线程安全隐患,并提供了使用synchronized关键字实现线程安全的标准解法。
多线程安检场景模拟
在飞机起飞前,旅客必须经过安全检查。我们可以将安检口类比为独木桥,同一时刻仅允许一人通过。为了模拟这一过程,我们创建一个包含登机牌和身份证验证逻辑的安检类,并在主程序中启动多个线程代表不同旅客同时通过安检。
第1步:定义非线程安全的安检核心类
首先编写 FlightSecurity 类,该类持有共享状态变量:计数器、登机牌字符串和身份证字符串。
package concurrent.FlightSecurity;
public class FlightSecurity {
private int count;
// 登机牌
private String boardingPass = "null";
// 身份证
private String idCard = "null";
public void pass(String boardingPass, String idCard) {
this.boardingPass = boardingPass;
this.idCard = idCard;
this.count++;
check();
}
private void check() {
// 简单测试:当登机牌和身份证首字母不相同时表示检查不通过
if (boardingPass.charAt(0) != idCard.charAt(0)) {
throw new RuntimeException("======Excepton======" + toString());
}
}
public String toString() {
return "The " + count + " paaaenges,boardingPass [" + boardingPass + "],idCard [" + idCard + "]";
}
}
在 pass 方法中,赋值操作 this.boardingPass = boardingPass 和 this.idCard = idCard 并非原子操作。如果在多线程环境下,一个线程正在更新这两个值,另一个线程可能介入读取已部分更新的状态,导致“张冠李戴”的数据不一致。
第2步:构建旅客线程与测试入口
创建继承自 Thread 的内部类 Passengers,用于模拟旅客持续通过安检的行为。在主函数中初始化三个线程,分别使用首字母相同的有效证件(A/A, B/B, C/C)进行并发测试。
package concurrent.FlightSecurity;
public class FightSecurityTest {
// 旅客线程
static class Passengers extends Thread {
private final FlightSecurity flightSecurity;
private final String idCard;
private final String boardingPass;
Passengers(FlightSecurity flightSecurity, String idCard, String boardingPass) {
this.flightSecurity = flightSecurity;
this.idCard = idCard;
this.boardingPass = boardingPass;
}
@Override
public void run() {
while (true) {
// 旅客不断地过安检
flightSecurity.pass(boardingPass, idCard);
}
}
}
public static void main(String[] args) {
// 定义三个旅客,身份证和登机牌首字母相同
final FlightSecurity flightSecurity = new FlightSecurity();
new Passengers(flightSecurity, "A", "A").start();
new Passengers(flightSecurity, "B", "B").start();
new Passengers(flightSecurity, "C", "C").start();
}
}
线程安全问题分析
运行上述代码后,控制台极易抛出 RuntimeException。典型的报错信息显示:登机牌字符与身份证字符不匹配,但计数却显示处理了大量旅客。例如:
java.lang.RuntimeException: ======Excepton======The 356 paaaenges,boardingPass [B],idCard [A]
这里出现了严重的逻辑错误:登机牌是 'B',身份证却是 'A'。这是因为:
1. 线程 T1 进入 pass,设置 boardingPass = "B"。
2. 线程 T1 尚未设置 idCard,线程 T0 插入并设置了 idCard = "A"。
3. 线程 T1 继续执行,读取到的 idCard 已被覆盖为 "A"。
4. check() 方法对比 "B" 和 "A",发现不匹配,抛出异常。
这种竞态条件(Race Condition)证明了原始 pass 方法不具备原子性,无法保证状态切换的一致性。
解决方案:使用 synchronized
要修复此问题,必须确保 pass 方法的执行是互斥的,即同一时刻只能有一个线程执行该方法内部的逻辑。最简单有效的方法是在方法签名上添加 synchronized 关键字。
修改后的方法如下:
public synchronized void pass(String boardingPass, String idCard) {
this.boardingPass = boardingPass;
this.idCard = idCard;
this.count++;
check();
}
加上 synchronized 后,JVM 会保证该方法的字节码指令序列在当前对象监视器下原子执行,其他线程必须等待前一个线程执行完毕(包括中间的赋值和检查)才能进入。这样便消除了数据交叉覆盖的风险,确保了安检逻辑的正确性。
总结
在线程安全的类设计中,共享可变状态的修改通常需要通过锁机制来保护。本例通过模拟机场安检这一具体场景,直观地展示了多线程并发下的数据竞争隐患。在实际开发中,对于类似的核心业务逻辑方法,务必评估其原子性需求,适时使用 synchronized 或更细粒度的锁控制手段来保障数据安全。
以上就是Java多线程模拟机场安检处理流程及线程安全解决的详细内容,更多关于Java 多线程并发编程的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









