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中内存存储的规则:
- 引用变量 vs 实际对象:
- Task1类中的
t是一个引用类型变量,它的作用是存储一个内存地址,指向堆内存中的Abs对象。 Abs obj = new Abs();语句里,new Abs()才是真正在堆内存中创建的Abs对象,obj只是指向这个对象的引用。
- Task1类中的
- 内存存储位置:
- 线程栈中存储的是线程执行时的局部变量、方法调用栈帧等,而Task1的实例成员
t属于Task1对象本身,Task1对象是创建在堆内存中的(包括t1和t2这两个Task1实例)。 - 关键是:你创建
t1和t2时,传入的是同一个obj引用,所以t1.t和t2.t这两个引用变量存储的是同一个堆内存地址,指向的是同一个Abs对象。
- 线程栈中存储的是线程执行时的局部变量、方法调用栈帧等,而Task1的实例成员
- 竞态条件的根源:
当两个线程调用t.increment()时,操作的是堆上同一个Abs对象的x变量,自然会出现共享资源的竞态问题,导致两次输出都是2的情况。
简单来说:不是每个线程拥有独立的Abs对象,而是两个线程的引用都指向了同一个Abs对象,所以操作的是同一份数据。
内容的提问来源于stack exchange,提问作者AAA
相关产品推荐
相关产品推荐

