java - 整数实例变量上的线程同步

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

最佳答案

TL;DR - 这是因为 Integer 池。使 ab 成为一个 Object(即 Object ab = new Object())以保证每个 Check 的锁定实例都不会干扰其他人。


一开始我也很疑惑。有趣的是,如果你改变

private Integer ab = 2;

private Object ab = new Object();

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

First thread variables

这是第二个线程。

Second thread variables

请注意,它实际上是同一个对象,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/

相关文章:

Java - 找不到逻辑错误

java - Java 9 如何避免拆分包

C++:如何简化线程锁?

c# - 用 IDisposable 包装互斥锁并测试它,但测试永远不会结束

java - 在 libgdx 中绘制一个矩形并增加其长度

java - JNA:如何在结构中指定可变长度(0+)数组?

java - 使用多线程读取单个文件

c++ - 多线程 Win32 C++ 程序在多线程中使用 try/catch 时崩溃

java - 单例模式 - 早期绑定(bind)(涉及静态变量)是否减少了互斥锁的需要?

c++ - 在 scoped_lock 上进行额外的解锁调用