You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无显式共享数据却出现线程不安全?多线程编程疑问

线程安全疑问:无显式共享数据却出现线程不安全问题

我是多线程编程新手,之前被告知线程不安全问题总是由多线程间的共享资源导致,这逻辑上说得通,但我的代码里好像没有线程间共享的数据,却出现了线程不安全问题。

代码示例

Outputer类

package test;
public class Outputer{
    
    public void output(){
        String name = "123456789";
        int len = name.length();
        for(int i=0;i<len;i++){
            System.out.print(name.charAt(i));
        }
        System.out.println();
    }
}

主类

package test;

public class TraditionalThreadSynchronized {

    public static void main(String[] args) {

        Outputer outputer = new Outputer();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <= 50; i++) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    outputer.output();
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <= 50; i++) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    outputer.output();
                }
            }
        }).start();
    }

}

异常输出情况

我预期每次输出都是完整的123456789,但实际有时会出现输出混乱:

... ...

123456789
123456789
123456789
123456789
123456789
123456789     // 预期输出
112323456789 // 异常输出
456789       // 异常输出
123456789   
123456789
123456789
123456789
123456789
... ...

我的理解

我知道问题出在这段非原子操作的代码块:

for(int i=0;i<len;i++){
    System.out.print(name.charAt(i));
}
System.out.println();

我已经通过给代码块加synchronized块修复了问题,现在输出正常。但我还有两个疑问:

  1. 「线程不安全问题总是由多线程间的共享资源导致」这一论断是否始终成立?
  2. 如果成立,我的示例中线程共享的是什么资源?如果不成立,还有哪些无共享数据的线程不安全场景?

问题解答

1. 该论断是始终成立的

线程不安全的本质是多个线程对共享资源的非同步访问,只要出现线程不安全问题,必然存在被多线程共享且未被正确同步的资源。

2. 你的示例中共享的资源是System.out(标准输出流)

你代码里的System.out.print()和System.out.println()操作的是JVM全局的标准输出流,这个流是所有线程共享的公共资源。虽然name是方法内的局部变量(线程私有),但输出依赖的PrintStream实例(System.out指向的对象)是全局共享的。

当两个线程同时调用print()时,它们会交替写入这个共享的输出流,导致字符被交叉输出,最终出现混乱结果。添加synchronized后,相当于给输出操作加了互斥锁,确保同一时间只有一个线程能操作输出流,从而避免了交叉写入。

3. 不存在完全无共享数据的线程不安全场景,但共享资源可能很隐蔽

有些场景下共享资源并非显式定义的变量,而是隐藏的系统或JVM内部资源:

  • 隐式共享的系统资源:比如标准输入输出流、文件句柄、网络套接字等,这些都是进程内所有线程共享的系统级资源。
  • JVM内部的共享状态:比如类加载器的状态、JIT编译器的缓存等,这些资源对开发者透明,但多线程操作时若未同步,也可能引发问题。
  • 指令重排序导致的协作异常:即使线程操作自己的局部变量,若涉及和其他线程的状态协作(比如通过volatile或锁传递状态),错误的指令重排序也可能导致逻辑异常,但本质还是因为线程间需要共享状态完成协作,只是状态传递过程出了问题。

内容的提问来源于stack exchange,提问作者Andy Li

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 12:10:38