我有一个提出翻译实用程序的课程。翻译本身应每30分钟重新加载一次。我使用Spring Timer支持。基本上,我的班级看起来像:
public interface Translator {
public void loadTranslations();
public String getTranslation(String key);
}
loadTranslations()可能需要很长时间才能运行,因此在运行时,旧的翻译仍然可用。这是通过在本地Map中加载翻译并在加载所有翻译时更改参考来完成的。
我的问题是:如何确保当线程已经加载翻译时,第二个也尝试运行,它会检测到并立即返回,而不会开始第二次更新。
一个synchronized方法只会对负载进行排队......我还在Java 1.4上,所以没有java.util.concurrent。
感谢您的帮助!
答案 0 :(得分:3)
使用某种形式的锁定机制仅在尚未进行任务时执行该任务。获取锁定令牌必须是一步到位的过程。参见:
/**
* @author McDowell
*/
public abstract class NonconcurrentTask implements Runnable {
private boolean token = true;
private synchronized boolean acquire() {
boolean ret = token;
token = false;
return ret;
}
private synchronized void release() {
token = true;
}
public final void run() {
if (acquire()) {
try {
doTask();
} finally {
release();
}
}
}
protected abstract void doTask();
}
如果任务同时运行,将测试将引发异常的代码:
public class Test {
public static void main(String[] args) {
final NonconcurrentTask shared = new NonconcurrentTask() {
private boolean working = false;
protected void doTask() {
System.out.println("Working: "
+ Thread.currentThread().getName());
if (working) {
throw new IllegalStateException();
}
working = true;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (!working) {
throw new IllegalStateException();
}
working = false;
}
};
Runnable taskWrapper = new Runnable() {
public void run() {
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
shared.run();
}
}
};
for (int i = 0; i < 100; i++) {
new Thread(taskWrapper).start();
}
}
}
答案 1 :(得分:1)
我来自.net背景(根本没有java经验),但是你可以尝试一种简单的静态标志,如果运行它的alrady,它会检查方法的开头。然后,您需要做的就是确保该标志的任何读/写都是同步的。所以在开始检查标志时,如果没有设置,设置它,如果设置,则返回。如果未设置,则运行方法的其余部分,并在完成后取消设置。只需确保将代码放在try / finally中,并将标记iunsetting放在finally中,以便在出现错误时始终取消设置。非常简化但可能就是您所需要的一切。
编辑:这实际上可能比同步方法更好。因为在它完成之前你真的需要一个新的翻译吗吗?如果必须等待一段时间,你可能不想长时间锁定一个线程。
答案 2 :(得分:0)
保持加载线程的句柄以查看它是否正在运行?
或者您不能只使用同步标志来指示负载是否正在进行中?
答案 3 :(得分:0)
这实际上与以经典方式完成时管理Singleton(gasp!)构造所需的代码相同:
if (instance == null) {
synchronized {
if (instance == null) {
instance = new SomeClass();
}
}
}
内部测试与外部测试相同。外部测试是我们不经常进入同步块,内部测试是确认自上次进行测试后情况没有改变(线程可能在进入同步之前被抢占)。
在你的情况下:
if (translationsNeedLoading()) {
synchronized {
if (translationsNeedLoading()) {
loadTranslations();
}
}
}
更新:这种构建单例的方法在JDK1.4下无法可靠地工作。有关解释see here。但是我认为你在这种情况下会好起来的。