本文介绍了如果只有一个线程写入而多个线程读取,是否需要添加一些锁或同步?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

说我有一个全局对象:

class Global {
   public static int remoteNumber = 0;
}

有一个线程定期运行以从远程获取新号码,并对其进行更新(仅写操作):

There is a thread runs periodically to get new number from remote, and updates it (only write):

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

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

And there are one or more threads using this global remoteNumber randomly (only read):

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

您可以看到我没有使用任何锁或synchronize来保护它,对吗?是否有任何可能引起问题的潜在问题?

You can see I don't use any locks or synchronize to protected it, is it correct? Is there any potential issue that might cause problems?

更新:

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

In my case, it's not really important that the reading threads must get the latest new value in realtime. I mean, if there is any issue (caused of lacking lock/synchronization) make one reading thread missed that value, it doesn't matter, because it will have chance to run the same code soon (maybe in a loop)

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

But reading a undetermined value is not allowed (I mean, if the old value is 20, the new updated value is 30, but the reading threads reads a non-existent value say 33, I'm not sure if it's possible)

推荐答案

您需要在此处进行同步(有一个警告,稍后再讨论).

You need synchronization here (with one caveat, which I'll discuss later).

主要问题是读取器线程可能永远看不到写入器线程进行的任何更新.通常,任何给定的写入最终都会被看到.但是在这里,您的更新循环非常简单,以至于写入可以轻松地保存在缓存中,而永远不会写入主内存.因此,您实际上必须在这里进行同步.

The main problem is that the reader threads may never see any of the updates the writer thread makes. Usually any given write will be seen eventually. But here your update loop is so simple that a write could easily be held in cache and never make it out to main memory. So you really must synchronize here.

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

EDIT 11/2017 I'm going to update this and say that it's probably not realistic that a value could be held in cache for so long. I think it's a issue though that a variable access like this could be optimized by the compiler and held in a register though. So synchronization is still needed (or volatile) to tell the optimizer to be sure to actually fetch a new value for each loop.

因此,您要么需要使用volatile,要么需要使用(静态)getter和setter方法,并且两种方法都需要使用synchronized关键字.对于这样的偶然写法,volatile关键字的重量轻得多.

So you either need to use volatile, or you need to use a (static) getter and setter methods, and you need to use the synchronized keyword on both methods. For an occasional write like this, the volatile keyword is much lighter weight.

需要注意的是,如果您真的不需要查看来自写线程的及时更新,则不必同步.如果无限期的延迟不会影响您的程序功能,则可以跳过同步.但是,在计时器上这样的事情看起来似乎不是删除同步的好用例.

The caveat is if you truly don't need to see timely updates from the write thread, you don't have to synchronize. If a indefinite delay won't affect your program functionality, you could skip the synchronization. But something like this on a timer doesn't look like a good use case for omitting synchronization.

根据 Java并发实践中的Brian Goetz的说法,Java/a JVM不允许向您显示不确定的"值-从未写入的值.从技术上讲,这些值称为凭空传播"的值,并且Java规范不允许使用它们.您可以肯定会看到以前对全局变量进行的写操作,要么是初始化时使用的零,要么是随后进行的写操作,但是不允许其他值.

Per Brian Goetz in Java Concurrency in Practice, it is not allowed for Java/a JVM to show you "indeterminate" values -- values that were never written. Those are more technically called "out of thin air" values and they are disallowed by the Java spec. You are guaranteed to see some write that was previously made to your global variable, either the zero it was initialized with, or some subsequent write, but no other values are permitted.

这篇关于如果只有一个线程写入而多个线程读取,是否需要添加一些锁或同步?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-01 18:24