在开始编码之前如何处理并发性 [已关闭]

我正在编写Java程序的中途,我正处于调试比我想要处理的更多的并发问题的阶段。

我不得不问:在制定程序时,你如何处理并发问题?就我而言,这是一个相对简单的游戏,但线程问题不断出现 - 任何快速修复几乎肯定会导致一个新问题。

用非常笼统的术语来说,在决定我的应用程序应该如何“流动”而不让我的所有线程都打结时,我应该使用什么技术?


答案 1

并发性归结为管理共享状态。

“所有并发问题都归结为协调对可变状态的访问。可变状态越少,就越容易确保线程安全。

因此,您必须问自己的问题是:

  • 我的应用程序需要哪些固有的共享数据?
  • 线程何时可以处理数据的快照,也就是说,它对共享数据的克隆进行瞬时工作?
  • 我能否识别已知模式并使用更高级别的抽象,而不是低级锁和线程协调,例如队列、执行器等?
  • 考虑一个全局锁定方案,以避免死锁并一致地获取锁

管理共享状态的最简单方法是序列化每个操作。然而,这种粗粒度的方法会导致高锁争用和性能差。管理并发性可以看作是一种优化练习,您可以在其中尝试减少争用。所以后续的问题是:

  • 最简单的方法是什么?
  • 我可以做出哪些简单的选择来减少争用(可能使用细粒度锁定)并提高性能,而不会使解决方案过于复杂?
  • 我什么时候变得过于细粒度,也就是说,引入的复杂性不值得提高性能?

许多减少争用的方法都依赖于某种形式的权衡,即强制实施正确行为所必需的内容与减少争用的可行方法。

  • 我在哪里可以放宽一些约束,并接受有时东西不会100%正确(例如计数器)?
  • 我是否可以保持乐观,只在发生并发修改时才处理冲突(例如,使用时间戳和重试逻辑 - 这就是 TM 所做的)?

请注意,我从未开发过游戏,只在企业应用程序的服务器端部分工作过。我可以想象它可以是完全不同的。


答案 2

我尽可能多地使用不可变的数据结构。我唯一一次使用可变结构是当我必须使用一个可以节省大量工作的库时。即便如此,我还是试图将该库封装在一个不可变的结构中。如果事情无法改变,那么就更少担心了。

我应该补充一点,在你未来的努力中要记住的一些事情是STM和Actor模型。这两种并发方法都显示出非常好的进展。虽然每个都有一些开销,但根据程序的性质,这可能不是问题。

编辑:

以下是一些可以在下一个项目中使用的库的链接。有Deuce STM,顾名思义,它是Java的STM实现。然后是ActorFoundry,顾名思义,它是Java的Actor模型。但是,我不禁用其内置的Actor模型为Scala制作了插头。


推荐