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变量,那么它如何成为两个不同对象的共享资源?
答案 0 :(得分:12)
TL; DR - 由于Integer
汇集而导致的。设为Object
(即Object ab = new Object()
)以确保Check
锁定的每个实例都不会干扰其他实例。
我最初也感到困惑。有趣的是,如果你改变了
private Integer ab = 2;
到
private Object ab = new Object();
同步消失(每次运行都会得到不同的输出)。以ab作为Integer
返回,我在调试模式下运行代码(在打印线程名称行上有一个断点),并找到以下内容。这是第一个主题:
这是第二个主题。
请注意,它实际上是同一个对象Integer@443
。即使您认为自己获得了两个不同的对象,两个Check实例中的两个ab
字段也会引用内存中的同一个对象!因此,是的,有正确的同步。那么,问题就是说Integer ab = 2
两次在内存中为你提供相同的Integer
对象。
通过说Integer ab = 2
,您正在使用 autoboxing ,原始值(类型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
。
答案 1 :(得分:4)
这是因为java中的 Ingeter Pool 概念。
介于-128和127之间的整数(包括在内)与字符串池的使用方式相同。
因此,当您使用private Integer ab = 2;
时,Check
的两个对象都会共享ab。
您可以使用值&gt; 128或任何其他类型的对象,以便您的代码不会同步。
您可以在此处查看答案:Why does the behavior of the Integer constant pool change at 127?以了解整数池概念。
答案 2 :(得分:1)
如果你看一下字节码,你可能会看到这段代码:
LINENUMBER 15 L1
ALOAD 0
ICONST_2
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
PUTFIELD com/example/Check.ab : Ljava/lang/Integer;
Java尝试通过调用2
类的#valueOf
将原始值Integer
设置为对象。我们知道,Integer
&#39; valueOf
实现了Flyweight模式。因此,实例之间共享相同的对象。