位置:首页 > Java > 线程安全实现原理与常见问题解析

线程安全实现原理与常见问题解析

时间:2026-08-15  |  作者:极客少年  |  阅读:0

多线程售票中的线程安全问题

上一节的售票案例中,很容易出现一些“意外”状况。比如,同一张票被重复打印,或者打出来的票号是0,甚至成了负数。

说到底,这些异常都源于多线程同时操作共享资源tickets,引发了典型的线程安全问题。

接下来在原有案例基础上做一版调整:模拟4个窗口出售10张票,并且在售票代码里,每卖出一张票都让线程休眠100毫秒,具体如文件1所示。

文件1 Example11.ja va

 1    // 定义SaleThread类实现Runnable接口
 2        class SaleThread implements Runnable {
 3        private int tickets = 10; // 10张票
 4        public void run() {
 5            while (true) {
 6                if (tickets > 0) {
 7                    try {
 8                        Thread.sleep(100); // 模拟售票耗时过程
 9                    } catch (InterruptedException e) {
 10                        e.printStackTrace();
 11                    }
 12                    System.out.println(Thread.currentThread().getName()
 13                            + " 正在发售第 " + tickets-- + " 张票 ");
 14                }
 15            }
 16        }
 17    }
 18    public class Example11 {
 19        public static void main(String[] args) {
 20            SaleThread saleThread = new SaleThread();
 21            // 创建并开启四个线程,模拟4个售票窗口
 22            new Thread(saleThread, "窗口1").start();
 23            new Thread(saleThread, "窗口2").start();
 24            new Thread(saleThread, "窗口3").start();
 25            new Thread(saleThread, "窗口4").start();
 26        }
 27    }

运行结果如图1所示。

线程安全实现原理与常见问题解析_wishdown.com

图1 运行结果

运行结果说明

从图1可以看到,最后几行打印出的票号出现了0和负数。

这种现象本不应该出现,因为售票程序中已经做了判断:只有当票号大于0时才会进行售票

之所以仍然出现负数票号,是因为多线程在售票时发生了安全问题。

问题出现的原因

在售票程序的while循环中加入sleep()方法后,就模拟了真实售票时“需要耗时”的场景。

问题恰恰就出在这个延迟过程。

当票号减到1时,假设此刻是窗口2线程准备出售1号票。它先完成票号判断,进入while循环。

但在真正售票之前,它会先通过sleep()方法模拟售票过程中的耗时操作。

就在这个时间空档里,窗口1线程也可能开始售票。此时票号依然还是1,所以窗口1线程同样会进入循环。

按照这个逻辑继续下去,4个线程都有机会进入while循环。

最终结果

等休眠结束后,这4个线程都会执行售票操作。

这样一来,就相当于同一张票被连续减了4次

  • 先卖出1号票

  • 然后继续打印0号票

  • 接着出现-1号票

  • 最后甚至出现-2号票

因此,运行结果中出现1、0、-1、-2这样的票号,本质上就是多线程同时访问共享资源所导致的线程安全问题。

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多