java - 使用时间戳更新的线程安全集?

标签 java multithreading

我有一个包含数千个电话号码的集合。当我的服务收到新帐户请求时,它会根据列表检查电话号码,以确保它不是已知的垃圾邮件号码。如果电话号码列表超过一周,该方法会从外部服务器获取列表的最新副本并将其读入内存。然后它更新“timestamp”变量以反射(reflect)列表的最后更新时间。像这样的东西:

public class SpamPhoneNumberManager() {

  private Set<String> spamPhoneNumbers;
  private long timestamp;

  public SpamPhoneNumberManager() {
    updateSpamPhoneNumbers();
  }

  public Set<String> getSpamPhoneNumbers() {
    if(timestamp - System.currentTimeMillis() > ONE_WEEK) {
      updateSpamPhoneNumbers();
    }
    return spamPhoneNumbers;
  }

  private void updateSpamPhoneNumbers() {
    Set<String> newSpamPhoneNumbers = new HashSet<>();
    //populate set from file on server
    spamPhoneNumbers = Collections.unmodifiableSet(newSpamPhoneNumbers);
    timestamp = System.currentTimeMillis();
  }

}

多个线程可以并发调用get()方法。在当前的实现中,我想不出任何并发问题。在我能想到的最坏情况下,列表由多个线程连续更新。是否需要使此线程安全?如果是这样,最好的方法是什么?

最佳答案

Is there a need to make this threadsafe?

您当前的类不是线程安全的,因为多个线程可以调用 getSpamPhoneNumbers() 并检查 if 条件,这不是原子的。 因此,多个线程尝试调用 updateSpamPhoneNumbers 导致竞争条件,因此会有中间状态,其中 spamPhoneNumbers 获得一个值和 timestamp不同的值(如果任何其他线程调用 get 方法发现并返回这些不一致的值,如下所述)。

简而言之,会出现如下情况:

Thread1 -> 使用 spamPhoneNumbersThread1 更新 spamPhoneNumbers 并设置 timestampThread1

Thread2 -> 更新 spamPhoneNumbersThread2(假设 timestamp 仍未更新)

Thread3 -> 调用 getSpamPhoneNumbers() 并且不进入 if block 并返回 spamPhoneNumbersThread2(针对 timestampThread1 进行验证)

这里的重点是明显存在竞争条件,您会看到不一致的(来自不同线程的)timestampspamPhoneNumbers 值。

If so, what's the best way to do this?

解决方案是您需要在 spamPhoneNumbers 对象上进行同步,以便一次只能有一个线程访问它。

public Set<String> getSpamPhoneNumbers() {
    synchronized(spamPhoneNumbers) {
      if(timestamp - System.currentTimeMillis() > ONE_WEEK) {
        updateSpamPhoneNumbers();
      }
    }
    return spamPhoneNumbers;
  }

  private void updateSpamPhoneNumbers() {
    Set<String> newSpamPhoneNumbers = new HashSet<>();
      //populate set from file on server
      spamPhoneNumbers = Collections.unmodifiableSet(newSpamPhoneNumbers);
      timestamp = System.currentTimeMillis();
  }

附注:您不需要在 updateSpamPhoneNumbers() 中进行任何同步,因为它是 private,但如果您以后改变主意并且这个方法变成了public,你必须在这里也需要同步。

关于java - 使用时间戳更新的线程安全集?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43299926/

相关文章:

java - 使用 ABBOTT & COSTELLO 自动测试 Web Start 程序

java - 使用android将图像上传到apache tomcat服务器

java - 在 GPS 上实现 A* 算法的开放和封闭列表

c++ - 在 C++ 中更快地浏览 csv 文件的方法

c++ - 使用 OpenMP 实现的线程池

java - Postgresql 和 JDBC 检查元数据中的 TYPE ENUM

java - 我必须在FutureTask中手动处理中断吗?

检查从 aaa..a 到 zzz..z 的每个 "word"

Python线程和多处理错误?

c# - Socket编程多客户端一台服务器