如果只有一个线程写入和多个线程读取,是否需要添加一些锁定或同步?

时间:2014-11-25 07:26:05

标签: java multithreading locking synchronized

假设我有一个全局对象:

class Global {
   public static int remoteNumber = 0;
}

有一个线程定期运行以从远程获取新号码并更新它(仅写入):

new Thread {
   @override
   public void run() {
       while(true) {
           int newNumber = getFromRemote();
           Global.remoteNumber = newNumber;
           Thread.sleep(1000);
       }
   }
}

并且有一个或多个线程随机使用此全局remoteNumber(仅读取):

int n = Global.remoteNumber;
doSomethingWith(n);

你可以看到我没有使用任何锁或synchronize来保护它,这是正确的吗?是否存在可能导致问题的潜在问题?


更新

就我而言,读取线程必须实时获取最新的新值并不重要。我的意思是,如果有任何问题(由于缺少锁定/同步)导致一个阅读线程错过了该值,那么这并不重要,因为它很快就会有机会运行相同的代码(可能在循环中)< / p>

但是不允许读取未确定的值(我的意思是,如果旧值为20,则新的更新值为30,但读取线程会读取不存在的值,如33,我不确定是否它是可能的)

2 个答案:

答案 0 :(得分:4)

你需要在这里进行同步(有一点需要注意,我稍后会讨论)。

主要问题是读者线程可能永远不会看到编写器线程所做的任何更新。通常最终会看到任何给定的写入。但是在这里你的更新循环是如此简单,以至于写入可以很容易地保存在缓存中,而不会将其发送到主内存。所以你真的必须在这里同步。

编辑11/2017 我要对此进行更新,并说可能在缓存中长时间保存一个值可能并不现实。我认为这是一个问题,尽管像这样的变量访问可以由编译器优化并保存在寄存器中。因此仍然需要同步(或volatile)来告诉优化器确保为每个循环实际获取新值。

因此,您需要使用volatile,或者需要使用(静态)getter和setter方法,并且需要在两种方法上使用synchronized关键字。对于偶尔这样的写作,volatile关键字 重量更轻。

需要注意的是,如果你真的不需要从写线程中看到及时更新,那么你就不必同步了。如果无限期延迟不会影响您的程序功能,则可以跳过同步。但是在计时器上这样的事情看起来不像是省略同步的好用例。

编辑:在 Java Concurrency in Practice 中,根据Brian Goetz,Java / JVM不允许向您显示&#34;不确定&#34;值 - 从未写过的值。这些在技术上被称为&#34;凭空而来#34; Java规范不允许使用这些值。您可以保证看到先前对您的全局变量进行的一些写操作,无论是初始化的零还是后续写入,都不允许其他值。

答案 1 :(得分:2)

读取线程可以读取未确定时间的旧值,但实际上没有问题。因为每个线程都有自己的变量副本。有时他们同步。您可以使用volatile关键字删除此优化:

public static volatile int remoteNumber = 0;