当前位置: 首页 > 面试题库 >

为什么在Boolean上同步不是一个好习惯?

黄修永
2023-03-14
问题内容

我的建筑师总是说

永远不要同步布尔值

我无法理解原因,如果有人可以举例说明为什么这不是一个好习惯,我将不胜感激。 参考样本代码

private Boolean isOn = false;
private String statusMessage = "I'm off";
public void doSomeStuffAndToggleTheThing(){

   // Do some stuff
   synchronized(isOn){
      if(isOn){
         isOn = false;
         statusMessage = "I'm off";
         // Do everything else to turn the thing off
      } else {
         isOn = true;
         statusMessage = "I'm on";
         // Do everything else to turn the thing on
      }
   }
}

问题答案:

我不明白为什么我们应该“从不同步布尔值”

你应该始终synchronize在一个常量对象实例上。如果你在分配的任何对象上进行同步(即,将对象更改为新对象),则该对象不是恒定的,并且不同的线程将在不同的对象实例上进行同步。由于它们在不同的对象实例上进行同步,因此多个线程将同时进入受保护的块,并且会发生竞争情况。这是同步于相同的答案Long,Integer等。

// this is not final so it might reference different objects
Boolean isOn;
...
synchronized (isOn) {
   if (isOn) {
      // this changes the synchronized object isOn to another object
      // so another thread can then enter the synchronized with this thread
      isOn = false;

更糟糕的是(如@McDowell指出的)任何Boolean是通过自动装箱创建(isOn = true)是同一个对象Boolean.TRUE(或.FALSE),这是在一个单ClassLoader跨所有对象。你的锁定对象应该在使用该类的类中是本地的,否则,你将锁定与其他类在发生相同错误时可能在其他锁定情况下锁定的同一个单例对象。

如果需要锁定布尔值,则正确的模式是定义一个private final锁定对象:

private final Object lock = new Object();
...

synchronized (lock) {
   ...

或者,你也应该考虑使用该AtomicBoolean对象,这意味着你可能根本不必synchronize使用它。

private final AtomicBoolean isOn = new AtomicBoolean(false);
...

// if it is set to false then set it to true, no synchronization needed
if (isOn.compareAndSet(false, true)) {
    statusMessage = "I'm now on";
} else {
    // it was already on
    statusMessage = "I'm already on";
}

在你的情况下,由于看起来你需要使用线程来打开/关闭它,所以你仍然需要synchronize在lock对象上设置布尔值,并避免测试/设置竞争条件:

synchronized (lock) {
    if (isOn) {
        isOn = false;
        statusMessage = "I'm off";
        // Do everything else to turn the thing off
    } else {
        isOn = true;
        statusMessage = "I'm on";
        // Do everything else to turn the thing on
    }
}

最后,如果你希望statusMessage可以从其他线程访问,则应将其标记为,volatile除非你synchronize在获取过程中也是如此。



 类似资料:
  • 问题内容: 方法链接 是对象方法返回对象本身以使结果被另一个方法调用的实践。像这样: 这似乎被认为是一种好习惯,因为它会产生可读的代码或“流畅的界面”。但是,对我而言,它似乎打破了面向对象本身所隐含的对象调用表示法-生成的代码不代表对先前方法的结果执行的动作,通常这是面向对象的代码的工作方式: 这种差异设法为“调用结果对象”的点标记创建了两种不同的含义:在链接的上下文中,以上示例将被视为保存参与者

  • 问题内容: 我已经开始学习线程同步。 同步方法: 同步块: 什么时候应该使用方法和块? 为什么块比方法更好? 问题答案: 这不是更好的问题,只是有所不同。 同步方法时,实际上是在与对象本身进行同步。对于静态方法,您正在同步到对象的类。因此,以下两段代码以相同的方式执行: 就像您写的一样。 如果要控制到特定对象的同步,或者只想将方法的 一部分 同步到该对象,则指定一个块。如果在方法声明上使用关键字,

  • 所以我理解为什么从异步返回空洞通常没有意义,但我遇到了一种我认为完全有效的情况。请考虑以下人为的示例: 我意识到这是一个不寻常的例子,但我试图使其简单化和更普遍化。有人能向我解释为什么这是可怕的代码,以及我如何修改它以正确遵循约定吗? 谢谢你的任何帮助。

  • 我真的到处都在寻找这个,我可以发出同步和异步数据请求,但我不能真正理解哪个是异步的,哪个是异步的?什么与什么同步?

  • 问题内容: 我经常看到有关不鼓励使用的其他问题的评论。为什么这样不好?有时我只是不在乎错误是什么,我只想继续编写代码。 为什么使用积木不好?是什么让它不好?是我pass出错还是我except出错了? 问题答案: 正如你正确猜到的那样,它有两个方面:通过在后面不指定任何异常类型来捕获任何错误,并在不采取任何操作的情况下简单地传递它。 我的解释要“长一点”,因此; 可以细分为: 不要发现任何错误。始终

  • 我刚刚发现,在react函数都是异步的,或者在调用它的函数完成后调用。 现在这两样东西很难消化 在博客中,函数是在函数内部调用的,但是触发函数的原因并不是被调用函数所知道的。 他们为什么要让异步,因为JS是单线程语言,而且这个setState不是WebAPI或服务器调用,所以只能在JS的线程上完成。他们这样做是为了使重新呈现不会停止所有事件侦听器和其他东西,还是有其他设计问题。