Java中布尔对象的值赋值会导致内存重新分配吗?

3
我有以下这段代码。
Boolean flag = new Boolean(false);
flag = true;

第二行代码(赋值语句)会在JVM中重新创建初始对象吗?我问这个问题是因为我正在使用一个Boolean对象来同步多个线程,如果发生重新初始化,我担心等待的线程将看不到值的改变。
在我的应用程序中,有多个线程都被给予了对先前Boolean对象的引用。只有一个线程将对象的值更改为true,其余线程则等待直到对象的值变为true。因此,如果T1是更改值的线程,其代码如下:
synchronized(flag) {
    flag = true;
    flag.notifyAll();
}

其余的线程(T2)将有如下代码:

synchronized(flag) {
    while(flag == false)
        wait();
    if(flag == true) {
        //do something
    }
}

因此,问题是在将true分配给flag后,其他线程(T2)是否仍然可以访问原始对象?
谢谢, Nick

1
好的,你最后一段话暗示了你的潜在问题。与其专注于关于你提出的解决方案的某个具体问题,你能否告诉我们更多关于你的问题,这样我们可以帮助你找到一个好的解决方案。http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem - Duncan Jones
2
我正在使用一个布尔对象来同步多个线程。这是指您正在获取布尔对象的锁定,还是仅将布尔对象用作标志? - Chetan Kinger
1
对于布尔字面量的装箱,结果值始终是Boolean.TRUEBoolean.FALSE,具体取决于字面量。 - Sotirios Delimanolis
@SotiriosDelimanolis 你所说的“被装箱”是什么意思? - nick.katsip
boolean 转换为 Boolean - Sotirios Delimanolis
显示剩余8条评论
3个回答

6
该赋值语句flag=false是一种装箱转换。它将被编译为flag=Boolean.valueOf(false),最终返回常量Boolean.FALSE
因此,答案是它不会创建新对象,但它将改变变量flag,因为它分配了一个与new Boolean(false)不同的实例。
不太清楚你实际在做什么,但通常,在可变变量上进行同步是有缺陷的设计。
问题在于您混淆了用于条件的值和用于同步的对象。更新后意图的最简实现是使用一个简单的布尔标志,并在包含该标志的实例上进行同步:
class WithFlag {
  private boolean flag;

  public synchronized void setToTrue() {
    if(!flag) {
      flag=true;
      notifyAll();
    }
  }
  public synchronized void waitForTrue() throws InterruptedException {
    while(!flag) wait();
  }
}

注意声明一个实例方法为 synchronized 类似于将其代码包装在 synchronized(this) { ... } 中。

那么,同步线程的替代更好的设计是什么?有什么建议吗? - nick.katsip
正如所说,我们不知道你在做什么。当我们不知道“它”是什么时,我们无法告诉你如何去做。 - Holger
非常感谢您对这篇文章的评论。帮助我更好地了解Java中的装箱操作。 - nick.katsip

2
如果您想使用布尔变量来同步线程,建议使用专门设计用于此目的的AtomicBoolean

1
其他答案已经解释了,当你说flag=false时,它是一个装箱转换,将返回常量Boolean.FALSE。另一个答案涵盖了但未强调的一个重要点是:当您在两个通过装箱转换分配相同值的布尔对象上获取锁定时,这与在一个布尔对象上获取锁定一样好。
我的答案试图给出一个示例来解释这一点。考虑创建两个线程并获取布尔锁定的以下代码。
public class BooleanTest {

    public static void main(String[] args) {
        BooleanTest test = new BooleanTest();
        test.booleanTest();

    }

    private void booleanTest() {
        BooleanLockTester booleanLock1 = new BooleanLockTester();
        booleanLock1.setBooleanLock(true);
        BooleanLockTester booleanLock2 = new BooleanLockTester();
        booleanLock2.setBooleanLock(true);
        BooleanLocker booleanLocker1 = new BooleanLocker(booleanLock1);
        BooleanLocker booleanLocker2 = new BooleanLocker(booleanLock2);

        Thread threadOne = new Thread(booleanLocker1);
        Thread threadTwo = new Thread(booleanLocker2);

        threadOne.start();
        threadTwo.start();
    }

    private class BooleanLocker implements Runnable {

        private BooleanLockTester booleanLockObj;

        public BooleanLocker(BooleanLockTester booleanLockObj) {
            this.booleanLockObj = booleanLockObj;
        }

        @Override
        public void run() {
            booleanLockObj.testLockOnBoolean();
        }

    }

    private class BooleanLockTester {

        private Boolean booleanLock = false;

        public synchronized void testLockOnBoolean() {
            synchronized (booleanLock) {
                for (int i = 0; i<1000000000; ++i) {
                    System.out.println(Thread.currentThread().getName());
                }
            }
        }

        public void setBooleanLock(Boolean booleanLock) {
            this.booleanLock = booleanLock;
        }

    }
}

在上面的例子中,这两个线程将永远无法同时进入for循环。当您运行程序时,您将看到先启动的线程将开始在控制台上打印,只有当它完成后,下一个线程才会开始打印到控制台。
让我们对上面的代码进行一些小改动: 更改代码中的以下行:
booleanLock2.setBooleanLock(true);

To this :

booleanLock2.setBooleanLock(false);

现在你会看到线程停止按顺序执行,并随机地在控制台中打印输出。这是因为线程现在获取了两个不同对象的锁。

1
字面上理解“当您在两个布尔对象上获得具有相同值的锁时,它就像获得一个布尔对象的锁一样”是错误的陈述。只有在同一实例上进行同步才有效。这是装箱转换的副作用,返回两个实例中的一个,使您的代码正常工作。如果您使用Boolean的构造函数创建实例(如问题中所示),则此方法将不起作用。尽管具有相同的值,但在通过Boolean(true)创建的两个不同实例上加锁将不起作用 - Holger
顺便提一句,我不明白你为什么要将示例分散在三个类中。这会让读者分心,远离重要部分。 - Holger
1
@Holger 我同意。那个句子是错的。我本来想说别的话。我编辑了我的回答。而且我不习惯把所有的东西都编码在一个类中。随便发表你自己的例子吧。 - Chetan Kinger
@bot 非常感谢您提供的信息。您的评论帮助我更清晰地理解了事情。 - nick.katsip

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接