【问题标题】:Can I synchronize reads of control variables?我可以同步控制变量的读取吗?
【发布时间】:2015-08-12 12:45:19
【问题描述】:

下面的代码可以运行,但我有点讨厌编写 isRunning() 方法:

class Test {
    private boolean running;
    public void startX() {
        synchronized(this) {
            running = true
        }
        while (isRunning()) {
            //do something
        }
    }
    public synchronized void stopX() {
        running = false;
    }
    private synchronized boolean isRunning() {
        return running;
    }
}

我能否以其他方式同步读取while (running){} 中的running 变量,还是必须编写isRunning() 方法?同样的问题也适用于其他控制变量,例如

for (;running;) {}

if (running) {}

在所有这些情况下,您似乎不得不编写一个毫无意义的方法来获得正确的同步。我错过了什么吗?

【问题讨论】:

  • 只需要running volatile

标签: java multithreading for-loop while-loop synchronized


【解决方案1】:

您可以使运行字段不稳定。使该字段易变使 JVM 注意到它应该对该字段的更改对其他线程可见。 “错过更新”警告适用于您想要读取一个值并基于该值进行更新的情况,这在这里似乎不适用。 多个线程可以写入该字段,如果他们所做的只是设置一个布尔标志,那么这将不是问题。

或者,如果您尝试取消线程,线程上已经为此提供了一个等效标志(并且可见性问题已得到处理)。您可以在线程上调用中断,Runnable 中的代码可以查询Thread.currentThread().isInterrupted() 以判断它是否被中断。这比使用您自己的标志更可取,因为如果线程正在等待或休眠,中断将导致线程唤醒。使用您自己的标志,您必须等到控制到达可以测试标志的地方。

【讨论】:

    【解决方案2】:

    如果您只是将running 的值重置一次以指定停止,则可以使用volatile 关键字。

    但是,如果您需要多次启动和停止,这将不起作用。这是因为 volatile 字段“可能会错过更新”

    这里是解释volatilelink 等情况下何时起作用的链接

    这是该链接中的代码示例,以防它失效:

    public class StoppableTask extends Thread {
       private volatile boolean pleaseStop;
    
       public void run() {
         while (!pleaseStop) {
           // do some stuff...
         }
       }
    
      public void tellMeToStop() {
         pleaseStop = true;
       }
      }
    

    如果需要多次启动和停止,则需要使用 Java 5 并发锁对象之一或显式同步

    【讨论】:

    • 感谢您的回答。不幸的是,我不能保证只有一个线程会写入这个值,所以它必须同步 AFAIK
    • @user1675642 volatile 保证不同线程之间的可见性,因此对于这种特殊情况不会造成任何麻烦。并且鉴于在执行期间写入pleaseStop 将始终为真,因此不会有更新未命中的机会。
    • @E_net4 pleaseStop 来自我链接到的代码示例博客。如果再次调用他们的startX(),OP 可能会重新设置标志。
    • @dkatzel 那么这将是与这个问题中的用例不同的用例,你不觉得吗?此外,我只是在回复 OP 关于不保证从单个线程写入的评论。
    【解决方案3】:

    只是为了补充其他人建议 volatile 的答案。 或者,您可以为检查创建一个类。 我已将变量设为static,因此所有线程都将指向同一个对象。

    class Runner{
    
     boolean static running=true;
    
     public static synchronized boolean getRunning(){
         return running;
     }
    
     public static synchronized boolean setRunning(boolean r){
         running=r;
     }
    }
    

    注意:

    如果您不需要全局变量,则删除 static

    【讨论】:

    • 如果可以的话,我永远无法通过使用可变字段来批准全局监视器同步。
    • @E_net4 这是一个见仁见智的问题。我有一个程序有超过 1000 个线程以相同的行为运行。到目前为止,还没有发生任何灾难性事件。
    • 不会改变它不是可扩展解决方案的事实。使其成为静态意味着您甚至不能创建它的多个实例。
    • @E_net4 running 不一定是静态的,这完全取决于场景。
    • 这个论点是公平的。但是现在您的解决方案是 atomic boolean 的实现,它已经在标准库中,并且可能会针对性能进行优化。
    猜你喜欢
    • 2017-10-06
    • 2018-04-22
    • 2014-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-01
    相关资源
    最近更新 更多