3

我已经阅读了Java Concurrency in Practice并留下了一个问题:当我使用 ConcurrentHashMap 时,我还需要担心本书第一部分中讨论的哪些数据并发问题?以下是我的一个程序中的几个示例:

1. 交易者的当前头寸(一个共享整数,其中“整数”是数学术语)

这个数字代表了一个trader对象当前拥有的东西并定义了它的状态。它必须阅读其位置以知道该做什么(寻找开始一个新位置,或管理当前位置)。 Trader方法在自己的线程上运行。

一个broker对象负责设置trader的位置。每次交易者的一个订单被执行时,它将设置头寸。 Broker方法在自己的线程上运行。

trader和都broker在同一个包中。Position 被实现为 package-private static ConcurrentHashMap。键是交易者对象的 id。值是整数。

包的外部是应用程序。它通过公共吸气剂间接获得交易者的头寸。

位置每隔几分钟最多会更改一次,因此broker不会经常触摸地图。但是,traderand 应用程序会经常读取。此外,我们经常有几个交易者同时阅读地图。

因此,以这种方式使用 ConcurrentHashMap,我不必处理锁定和数据可见性?ConcurrentHashMap 处理所有事情?

2. 市场(买、卖、最后价)

与位置几乎相同的情况,除了现在broker会非常频繁地更新价格(在繁忙时间每秒最多更新 10 次;通常每秒更新几次)。和trader应用程序仍然会频繁读取。地图键现在是指示哪个股票或期货的代码,而值是保存市场价格的对象。

它似乎工作正常,但在阅读 JCIP 之后,我意识到如果事情没有正确实施,程序仍然可能被破坏。这本书讨论了 ConcurrentHashMap,但没有明确告诉我第一部分中的哪些问题我们不再需要手动解决。在这种情况下,我似乎不需要做synchronize任何事情。那是对的吗?

4

3 回答 3

4

ConcurrentHashMap的,只要:

  • map 持有的值是不可变的。鉴于您的价格对象是不可变的,您的描述似乎是正确的;
  • 您在 map 上没有必须是原子的操作,并且不能表示为对 map API 的单个调用。例如,如果您需要像“从映射中读取值、执行计算并将结果放回映射”这样的操作是原子的,您仍然需要在此操作期间保持显式锁定,或者更好的是,更改应用程序以便您只使用地图 API 的原子操作,例如get/put/putIfAbsent.
于 2010-12-29T18:41:19.363 回答
4

因此,以这种方式使用 ConcurrentHashMap,我不必处理锁定和数据可见性?ConcurrentHashMap 处理所有事情?

这取决于地图中的内容,如果我正确阅读了您的示例,情况如下所示

static final ConcurrentMap<Integer,Integer> map = ...

class Trader{

  public int doRead(){
      map.get(someId);
   }
}
class Broker{
   public void doWrite(){
      map.put(someId,someValue);
   }
}

如果是这种情况,那么是的,所有的并发都得到了处理。

但是,如果地图看起来像

static final ConcurrentMap<Integer,Trader> map = ..

    class Broker{
       public void doWrite(){
          map.get(someId).setPosition(somePosition);
       }
    }

这不是线程安全的,即使您放置时 ConcurrentHashMap 锁定,此时对象的所有并发访问都必须处理它们自己的同步。

于 2010-12-29T18:54:17.030 回答
3

除非您谈论每秒超过 100,000 次更新/读取(非常粗略的指南),否则我不会考虑使用多个线程。原因是线程安全的组件比非线程安全的组件花费的时间要长很多倍。因此,如果一个组件需要 5 倍的时间来保证线程安全,那么您需要同时使用超过 5 个线程才能达到收支平衡,更别提更快了。

当您要执行相对昂贵的操作时,多线程会更有用。作为一个线程,更新仓位或价格要高效得多。

于 2010-12-29T21:06:55.740 回答