【问题标题】:ArrayList inside HashTable is thread safeHashTable 中的 ArrayList 是线程安全的
【发布时间】:2013-01-17 13:49:16
【问题描述】:

简单的问题。下面的代码是线程安全的吗...?

  1. 如果是,有没有更好的方法..?
  2. 如果不是,为什么。?以及如何使它成为线程安全的。

我的主要疑问是因为ArrayListHashtable 中,因为ArrayList 不是线程安全的。所以如果它是Hashtable 的一部分,也会发生同样的事情。

Hashtable<Thread, List<String>> threadObjects = new Hashtable<Thread, List<String>>();
 // lets assume some object is added. 
 synchronized (threadObjects)
  {
     thread = Thread.currentThread();
     List<String> v =  threadObjects.get(thread);
     if (null != v)
     {
       // do something
     }
  }

谢谢

【问题讨论】:

  • 为什么不将列表作为参数传递给您的线程?
  • 1.您发布的部分是线程安全的,但这完全取决于您如何处理列表。 2. 不要使用Hashtable,因为它已经过时了。例如,使用ConcurrentHashMap,或仅使用Collections.synchronizedMap(new HashMap())
  • @fge 不,我不想要。
  • 如果threadObjects 不是本地引用,则应声明为final 以清楚起见并避免代码的其他部分意外覆盖。
  • 实际上,没有足够的代码来确定您想要做什么以及您的数据将在哪里暴露给其他线程

标签: java multithreading arraylist concurrency thread-safety


【解决方案1】:

如果是,有没有更好的方法..?

是的,但使用 ThreadLocal>

我的主要疑问是 Hashtable 中的 ArrayList,因为 ArrayList 不是线程安全的。

通常你是对的,但是这段代码确保了一个 ArrayList 只能被一个线程访问。当然,如果您不遵循此模式,那将是线程安全的。

注意:synchronized (threadObjects) 在这种情况下没有任何区别。

【讨论】:

    【解决方案2】:

    下面的代码是线程安全的吗...?

    是的……前提是:

    • 创建映射并向映射添加条目的代码也是线程安全的,
    • 没有其他代码在不同步地图的情况下使用地图(根据您的代码),并且
    • 没有代码更新列表对象而不在地图上同步。

    外部同步数据结构的线程安全性只能通过检查所有使用该数据结构的代码来确定。

    正如 Peter 所说,ThreadLocals 会是一个更好的解决方案,但如果列表对象在没有适当锁定的情况下发生变异,仍然存在潜在的线程安全问题。

    【讨论】:

      【解决方案3】:

      您可以使用 ThreadLocal 来存储特定于线程的数据。例如:

      private final ThreadLocal<List<String>> threadObjects =
          new ThreadLocal<List<String>>() {
              @Override protected List<String> initialValue() {
                  return new ArrayList<String>();
              }
          };
      
      public void foo() {
          for (String s : threadObjects.get()) {
              // do something with each string in this thread's list
          }
      }
      

      这是一种比Hashtable 方法更好的跟踪线程特定数据的方法,因为表是在 ThreadLocal 实现本身内部为您维护的,包括同步。只要不发布对其他线程的引用,ArrayList 实例就不需要额外的同步;每个线程都有自己的 ArrayList 实例,可以随意使用,无需担心并发问题。

      覆盖initialValue() 是一种方便,因为它允许您避免在threadObjects.get() 的结果上检查null,当然前提是您没有将本地线程显式设置为null

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-01-15
        • 2019-12-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-24
        • 2010-09-22
        • 2011-02-10
        相关资源
        最近更新 更多