
多线程卖票问题一、问题假设共有固定数量的车票开启多个线程模拟多个售票窗口同时售票如果不做特殊处理会出现同一张票被多次卖出、票数超卖的异常情况。二、线程基础概念线程是进程的运行子分支同一个进程内的多个线程会共享进程中的数据。线程安全问题的根源比如多个线程同时修改同一份共享数据时操作过程可能被 CPU 打断最终导致数据结果出现问题。三、创建线程的两种方式方式 1继承 Thread 类自定义类继承 Thread 类重写 run() 方法编写线程要执行的业务逻辑创建自定义类对象调用 start() 方法启动线程方式 2实现 Runnable 接口自定义类实现 Runnable 接口重写 run() 方法编写线程执行逻辑将自定义类的对象传入 Thread 类的构造方法调用 Thread 对象的 start() 方法启动线程四、线程内存模型堆内存所有线程共享静态变量、共享数据都存储在这里比如总票数 count。栈内存每个线程私有存放线程自己的局部变量。线程操作共享数据时会先把数据复制到自己的本地变量副本中修改完成后再写回共享内存。普通的数值增减不是原子操作执行中途可以被 CPU 打断因此多线程同时操作就会出现数据错误。线程内存模型示意图JVM栈内存线程私有堆内存线程间共享线程 B线程 Acount 副本countcount 副本副本--副本--五、线程安全解决方案synchronized 内置锁作用将一段代码包裹起来保证同一时刻只有一个线程能执行这段代码让操作变成不可打断的原子操作。尽可能避免出现比如同一张票被多次卖出、票数超卖的异常情况语法javasynchronized(锁对象) {// 需要保证原子性的核心代码}注意点锁对象必须是所有线程都能访问到的同一个对象通常用静态对象充当全局锁。锁的粒度要尽量小只包裹核心的共享数据操作代码锁的范围太大会导致其他线程长时间等待降低程序运行效率。线程进入同步代码块时自动获取锁代码执行完毕后自动释放锁。六、卖票代码核心逻辑定义静态的总票数 count 和锁对象 obj保证所有线程共享同一份数据和锁。每个售票线程循环售票在修改票数前加锁并且加锁后再次判断票数是否大于 0防止超卖。减票操作完成后再输出售票信息保证输出的数据准确。小结卖票问题的核心是多线程共享数据的安全问题通过 synchronized 内置锁可以保证售票操作的原子性有效避免超卖、重复售票的异常。