Integer实例变量上的线程同步

时间:2016-06-30 08:21:31

标签: java multithreading thread-safety

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变量,那么它如何成为两个不同对象的共享资源?

3 个答案:

答案 0 :(得分:12)

TL; DR - 由于Integer汇集而导致的。设为Object(即Object ab = new Object())以确保Check锁定的每个实例都不会干扰其他实例。

我最初也感到困惑。有趣的是,如果你改变了

private Integer ab = 2;

private Object ab = new Object();

同步消失(每次运行都会得到不同的输出)。以ab作为Integer返回,我在调试模式下运行代码(在打印线程名称行上有一个断点),并找到以下内容。这是第一个主题:

First thread variables

这是第二个主题。

Second thread variables

请注意,它实际上是同一个对象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模式。因此,实例之间共享相同的对象。