使用时间戳更新的java线程安全集?
我有一套可以装上千个电话号码。当我的服务收到新的帐户请求时,它会根据列表检查电话号码,以确保它不是已知的垃圾邮件号码。如果电话号码列表超过一周,该方法将从外部服务器获取列表的最新副本并将其读入内存。然后更新“timestamp”变量以反映列表的最后更新时间。比如:
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()方法。在当前的实现中,我想不出任何并发问题。在我能想到的最糟糕的情况下,列表由多个线程连续更新。是否需要使此线程安全?如果是这样,最好的方法是什么
# 1 楼答案
如果您只想将某些项目添加到您的电话号码集合中,则使用任何线程安全的集合类型(例如Synchronized Set)声明您的集合就足够了
阅读更多信息:Using Synchronized with Thread-Safe Collection?
# 2 楼答案
我会改变你的执行方式。使用Timer作为后台线程,以一周的固定速率更新目录,或使用ScheduledExecutorService
下面是一个使用计时器的示例
# 3 楼答案
您当前的类不是线程安全的,因为多个线程可以调用
getSpamPhoneNumbers()
并检查if
条件,该条件不是原子的。 因此,多个线程尝试调用updateSpamPhoneNumbers
导致争用条件,因此将存在中间状态,在spamPhoneNumbers
中获得一个值&timestamp
使用不同的值(如果任何其他线程调用get
方法找到并返回这些不一致的值,如下所述)简言之,将出现如下情况:
螺纹1->;用
spamPhoneNumbersThread1
更新spamPhoneNumbers
并设置timestampThread1
螺纹2->;更新
spamPhoneNumbersThread2
(假设仍然timestamp
未更新)螺纹3->;调用
getSpamPhoneNumbers()
,不输入if块并返回spamPhoneNumbersThread2
(根据timestampThread1
验证)这里重要的一点是,显然存在竞争条件,您将看到不一致的(来自不同线程)
timestamp
和spamPhoneNumbers
值解决方案是需要对
spamPhoneNumbers
对象进行同步,以便一次只有一个线程可以访问它p.S.:您不需要在
updateSpamPhoneNumbers()
内进行任何同步,因为它是private
,但是如果您将来改变主意,并且此方法变为public
,您也必须在此处进行同步