Java多线程模拟机场安检:线程安全问题与同步解决

Dylan
Dylan
发布时间:2026-10-16 | 2人浏览过
原创

本文利用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 多线程并发编程的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表