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

Java线程间共享实例的困惑:为何同一Abs对象被多线程共享?

Java线程共享对象的疑问

示例代码

Abs类

class Abs
{
    int x = 0;
    public void increment ( )
    {
        x++;
        System.out.println( x );
    }
}

Task1线程类

class Task1 extends Thread
{
    Abs t;
    public Task1 ( Abs x ) { t = x; }
    public void run ( ) { t.increment(); }
}

Multi主类

public class Multi
{
    public static void main ( String args[] )
    {
        Abs obj = new Abs();
        Task1 t1 = new Task1( obj );
        Task1 t2 = new Task1( obj );

        t1.start();
        t2.start();
    }
}

问题背景

上述代码中,obj的成员变量x会在线程t1和t2间共享,可能引发竞态条件,出现如下输出:

2
2

原因是obj存储在堆内存中,被两个线程共享。

用户困惑

每个Task1线程都拥有一个Abs类型的实例成员t,构造方法中赋值为传入的Abs对象(t=x;)。认为每个线程的实例成员位于线程栈中,所以每个线程会拥有独立的Abs对象,obj不会被共享。想知道为何实际是共享的?


解答

你核心的误区是混淆了引用变量和实际对象的区别,以及Java中内存存储的规则:

  1. 引用变量 vs 实际对象:
    • Task1类中的t是一个引用类型变量,它的作用是存储一个内存地址,指向堆内存中的Abs对象。
    • Abs obj = new Abs();语句里,new Abs()才是真正在堆内存中创建的Abs对象,obj只是指向这个对象的引用。
  2. 内存存储位置:
    • 线程栈中存储的是线程执行时的局部变量、方法调用栈帧等,而Task1的实例成员t属于Task1对象本身,Task1对象是创建在堆内存中的(包括t1和t2这两个Task1实例)。
    • 关键是:你创建t1和t2时,传入的是同一个obj引用,所以t1.t和t2.t这两个引用变量存储的是同一个堆内存地址,指向的是同一个Abs对象。
  3. 竞态条件的根源:
    当两个线程调用t.increment()时,操作的是堆上同一个Abs对象的x变量,自然会出现共享资源的竞态问题,导致两次输出都是2的情况。

简单来说:不是每个线程拥有独立的Abs对象,而是两个线程的引用都指向了同一个Abs对象,所以操作的是同一份数据。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 01:05:17