Java 1.4同步:只允许一个方法实例运行(非阻塞)?

我有一个类提出翻译实用程序。 翻译本身应每30分钟重新加载一次。 我使用Spring Timer支持。 基本上,我的class级看起来像:

public interface Translator { public void loadTranslations(); public String getTranslation(String key); } 

loadTranslations()可能需要很长时间才能运行,因此在运行时,旧的翻译仍然可用。 这是通过在本地Map中加载翻译并在加载所有翻译时更改参考来完成的。

我的问题是:我如何确保当一个线程已经加载翻译时,第二个也尝试运行,它会检测到并立即返回,而不开始第二次更新。

一个synchronized方法只会对负载进行排队……我还在Java 1.4上,所以没有java.util.concurrent。

谢谢你的帮助 !

使用某种forms的锁定机制只执行任务(如果尚未进行)。 获取锁定令牌必须是一步到位的过程。 看到:

 /** * @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(); } 

如果任务同时运行,将测试将引发exception的代码:

 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(); } } } 

我来自.net背景(根本没有java经验),但是你可以尝试一种简单的静态标志,如果它的运行时它会检查方法的开头。 然后,您需要做的就是确保该标志的任何读/写都是同步的。 所以在开始时检查标志,如果没有设置,设置它,如果设置,则返回。 如果未设置,则运行方法的其余部分,并在完成后取消设置。 只需确保将代码置于try / finally中并将标志置于finally中,以便在出现错误时始终取消设置。 非常简化但可能是您所需要的。

编辑:这实际上可能比同步方法更好。 因为在它完成之前你真的需要一个新的翻译吗? 如果必须等待一段时间,你可能不想长时间锁定一个线程。

保持加载线程的句柄,看它是否正在运行?

或者您不能只使用同步标志来指示负载是否正在进行中?

这实际上与以经典方式完成时管理Singleton(gasp!)构造所需的代码相同:

 if (instance == null) { synchronized { if (instance == null) { instance = new SomeClass(); } } } 

内部测试与外部测试相同。 外部测试是我们不经常进入同步块,内部测试是确认自我们上次进行测试后情况没有改变(线程可能在进入同步之前被抢占)。

在你的情况下:

 if (translationsNeedLoading()) { synchronized { if (translationsNeedLoading()) { loadTranslations(); } } } 

更新:这种构建单例的方法在JDK1.4下无法可靠地工作。 有关解释请参见此处 。 但是我认为你在这种情况下会好起来的。