public class Main{
public static void main(String[] args) throws Exception {
// Creating objects for class Check(2 different objects)
Check c = new Check("s1");
Check c1 = new Check("s2");
c.start();c1.start();
}
}
class Check extends Thread{
Check(String name){super(name);}
private Integer ab = 2;
public void run(){
synchronized (ab) {
System.out.println(Thread.currentThread().getName());
for(int i=0;i<10;i++)System.out.print(i+" ");
}
}
}
这里我同步了变量 ab。我也创建了类 Check 的两个不同实例,但我总是得到 s1 的输出,然后是 s2,反之亦然,但没有混合,为什么会这样?当我已经创建了两个单独的对象(在 main 中),所以两个不同的线程,两个不同的 ab 变量,那么它如何成为两个不同对象的共享资源?
最佳答案
TL;DR - 这是因为 Integer
池。使 ab 成为一个 Object
(即 Object ab = new Object()
)以保证每个 Check
的锁定实例都不会干扰其他人。
一开始我也很疑惑。有趣的是,如果你改变
private Integer ab = 2;
到
private Object ab = new Object();
同步消失了(每次运行都会得到不同的输出)。返回 ab 作为 Integer
,我在 Debug模式下运行您的代码(在打印线程名称行上有一个断点)并发现以下内容。这是第一个线程:
这是第二个线程。
请注意,它实际上是同一个对象,Integer@443
。即使您认为您得到了两个不同的对象,但两个 Check 实例中的 ab
字段都指向内存中的同一个对象!因此,是的,有正确的同步。那么,问题是如何说 Integer ab = 2
两次让您在内存中得到相同的 Integer
对象。
通过说 Integer ab = 2
,您正在使用自动装箱,通过它自动将原始值(int
类型)转换为相应的对象类型 Integer
。这相当于调用自动装箱方法调用:
private Integer ab = Integer.valueOf(2);
如果我们查看 Integer.valueOf
,我们会注意到它有一个特定范围内的值池:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
对于大多数常规设置,这将包括值 2
。因此,当您调用此方法时,您将在内存中获得相同的 Integer
值。
关于java - 整数实例变量上的线程同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38117630/