- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我有一个包含数千个电话号码的集合。当我的服务收到新帐户请求时,它会根据列表检查电话号码,以确保它不是已知的垃圾邮件号码。如果电话号码列表超过一周,该方法会从外部服务器获取列表的最新副本并将其读入内存。然后它更新“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
进行验证)
这里的重点是明显存在竞争条件,您会看到不一致的(来自不同线程的)timestamp
和 spamPhoneNumbers
值。
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/
我是一名优秀的程序员,十分优秀!