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

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

南门鸿哲
2023-03-14
问题内容

说我有一个全局对象:

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进行保护,对吗?是否有可能引起问题的潜在问题?

更新:

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

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


问题答案:

您需要在这里进行同步(有一个警告,我将在后面讨论)。

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

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

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

需要注意的是,如果您真的不需要查看来自写入线程的及时更新,则不必进行同步。如果无限期的延迟不会影响您的程序功能,则可以跳过同步。但是在计时器上这样的事情看起来并不适合用来省略同步。

编辑:Per Brian Goetz在 Java Java Concurrency in Practice中 ,不允许Java / a
JVM向您显示“不确定”值-从未写入的值。从技术上讲,这些值称为“凭空传播”的值,并且Java规范不允许使用它们。您可以确保看到先前对全局变量进行的写操作,它是初始化时使用的零,也可以是后续的写操作,但不允许其他值。



 类似资料:
  • 假设: 只有一个特定的线程设置了某个引用字段(不是长或双精度,所以写入它是原子的) 有任意数量的线程可能会读取同一个字段 稍微陈旧的读取是可以接受的(最多几秒钟) 在这种情况下,您需要挥发性或原子参考或类似的东西吗? 该条指出: 如果您严格遵守单一写入器原则,则不需要内存障碍。 这似乎表明,在我描述的情况下,你真的不需要做任何特殊的事情。 我做了一个测试,结果很奇怪: 有时运行this会输出“线程

  • 问题内容: 线程都是可运行的,并且它们拥有相同的锁。两个线程都可以运行时,它们可以锁定相同的地址吗?那是JRE错误吗? 问题答案: 该问题仅存在于线程转储中。实际上,在任何时间点,锁都仅由一个线程持有。但是,线程转储显示两个具有相同锁的不同线程,因为它不是原子的。 可以使用以下程序轻松重现该行为:

  • 我有一个由线程a读取和更新的同步映射(通过< code > collections . synchronized Map()),线程B只能通过< code>Map.keySet()(只读)访问该映射。 我应该如何同步这个?文档中说key Set()(用于Collections.synchronized映射)“不需要在同步块中”。我可以把线程A的读/写访问放在同步块中,但这有必要吗? 我想,如果Ma

  • 在Flink中,像“平面地图”、“地图”等运算符称为任务,如果我将平面地图的并行度设置为30,那么这个任务有30个子任务。 现在,如果我只有一个插槽,它会在一个插槽中产生多个线程吗?还是每个插槽只有一个线程? Flink会在该插槽中简单地创建30个线程,还是使用类似线程池的东西? 以上不是一个恰当的例子。 假设在作业中我有操作符flatMap和map,它们都有并行度1,我只有一个插槽,这个插槽会创

  • 我有一张地图。假设: 多个线程正在访问此映射,但是每个线程只能访问映射中自己的条目。这意味着,如果线程T1将对象A插入到映射中,则保证没有其他线程将访问对象A。最后,线程T1也将删除对象A。 还可以保证没有线程会遍历映射。 这张地图需要同步吗?如果是,您将如何同步?(ConcurrentHashMap、Collections.synchronizedMap()或synchronized block

  • 我对同步块有一些疑问。在提问之前,我想分享另一个相关帖子链接的答案,以回答相关问题。我引用彼得·劳里的同一个答案。 > <块引号> 同步确保您对数据有一致的视图。这意味着您将读取最新值,其他缓存将获得最新值。缓存足够智能,可以通过特殊总线相互通信(JLS不需要,但允许)该总线意味着它不必触摸主存储器即可获得一致的视图。 如果您只使用同步,则不需要Volatile。如果您有一个非常简单的操作,而同步