高并发必备篇(二)——线程为什么会不安全?
发布日期:2021-05-18 10:52:49 浏览次数:21 分类:精选文章

本文共 1854 字,大约阅读时间需要 6 分钟。

为什么会出现线程不安全——深入理解多线程环境下的竞态条件

在之前的案例中,我们通过创建三个窗口线程的卖票系统,观察到票价数据重复的现象。这引发了我们对“线程不安全”这一概念的好奇。那么,为什么多线程应用会存在不安全性呢?我们从以下几个方面来探讨这一现象。

1. Java线程安全的定义

线程安全的概念最早由Brian Goetz在其著作《Java并发编程实战》中提出。线程安全指的是,在多个线程同时访问一个对象时,由于线程调度的不确定性,缺乏额外的同步机制时,对象的行为可能会导致不正确的结果。然而,在这个卖票系统的案例中,即使没有额外的同步机制,票价数据却显示出重复,这正是线程不安全的体现。


2. 对象的有状态与无状态性

在Java中,对象可以分为两种类型:有状态和无状态。

无状态对象(Stateless Bean)

无状态对象不含实例变量,也不依赖其他类的实例变量。由于其没有存储数据的能力,所以在多线程环境下,它们的行为不会受到其他线程的影响,而且它们的操作结果总是唯一确定的。因此,无状态对象在多线程环境下是线程安全的。

有状态对象(Stateful Bean)

有状态对象则完全不同了。它拥有实例变量,能够保存数据并反映实时状态。这些实例变量存储在JVM的堆内存中,而堆内存是共享的。因此,当多个线程同时修改这些共享实例变量时,就可能产生竞态条件,导致线程不安全。

而我们之前的卖票案例,就正是有状态对象在多线程环境下被多次修改所导致的。


3. 竞态条件的本质

竞态条件是由于多个线程同时访问同一共享资源(如我们的票价对象)时,由于线程调度顺序的不确定性产生的。一个简单的示例是,线程A在修改票价的同时,线程B读取票价。如果线程B在修改票价之前读取到票价,就会导致票价重复的现象。

我们可以通过分析卖票系统的执行顺序,来进一步理解竞态条件:

  • 线程1(窗口1)检查票价是否大于0,并打印票价。
  • 线程2(窗口2)在同一时间打印当前的票价,此时票价仍为100。
  • 线程1在线打印完成后,将票价减1。
  • 在这种情况下,线程2先打印票价100,线程1在完成打印后减1,导致票价重复。
  • 这种执行顺序违反了我们的期望,打印了重复票价的现象。


    4. 指令重排与有序性

    在计算机科学中,指令重排是为了提高性能而采取的优化措施。它的本质是允许CPU根据需要调整指令的执行顺序,以充分利用处理器资源。然而,在多线程环境下,这种指令重排会产生新的问题。

    有限制的有序性

    在单线程环境下,代码的执行顺序是由程序的逻辑严格决定的。但在多线程环境下,因为CPU的指令重排和时间片轮转的不可预测性,操作的执行顺序可能与逻辑设计不符。这种“不可预测性”就是我们所谓的“有序性”问题。

    一个经典案例

    假设有一个简单的代码段:

    public class Testing {
    public static void main(String[] args) {
    int a = 1;
    int b = 2;
    int c = a + b; // 步骤1
    a = 3;
    c = a + b; // 步骤2
    }
    }

    在单线程环境下,变量的改变顺序很清楚:

  • 步骤1先执行
  • 步骤2执行时,a已经被修改为3
  • 但是,在多线程环境下,步骤1和步骤2可能会并行执行:

  • CPU先执行步骤1(计算c=a+b),然后立即开始执行步骤2(c = a + b)。
  • 中间可能存在时间片轮转,其他线程也会占用CPU。
  • 在这种情况下,步骤1和步骤2可能交替执行,导致不同的结果。

  • 为什么多线程会出现不安全?

    多线程环境下的主要问题在于,多个线程共享相同的内存资源(如堆内存中存储的对象),而对这些资源的写读操作可能同时进行。这种情况下,就会出现:

  • 竞态条件:不同的线程在不恰当的时序下执行读写操作。
  • 指令重排:由于CPU对指令的重新排序,会改变代码的实际执行方式。
  • 由于线程不控制其自身的上下文切换和时间片分配,多线程环境下的资源共享必然会引入不确定性。


    总结

    通过分析线程安全的概念、有状态对象的特性、竞态条件以及指令重排的影响,我们逐渐理解了多线程环境下为什么会出现不安全。关键在于,多线程共享的是同一套内存资源,而对这些资源的修改和读取操作可能同时进行,这违反了线程的安全假设。

    接下来的内容,我们将探讨Java线程模型和内存模型的具体实现,这将帮助我们更深入地理解以上问题。

    上一篇:程序员就地过年,怎样才能更有意义?
    下一篇:QQ翻车微信出bug,程序员该如何应对?

    发表评论

    最新留言

    第一次来,支持一个
    [***.219.124.196]2025年04月11日 21时05分15秒