Java 1.4同步:仅允许方法的一个实例运行(非阻塞)?
我有一堂课提议翻译实用程序。翻译本身应每30分钟重新加载一次。我为此使用Spring Timer支持。基本上,我的课看起来像:
public interface Translator { public void loadTranslations(); public String getTranslation(String key); }
loadTranslations()可能需要很长时间才能运行,因此在运行时,仍然可以使用旧的翻译。这是通过将翻译加载到本地地图中并在加载所有翻译后仅更改参考来完成的。
我的问题是:如何确保当一个线程已经在加载翻译时,第二个线程也尝试运行,它会检测到该线程并立即返回,而不启动第二个更新。
同步方法只会使负载排队...我仍然在Java 1.4上,所以没有java.util.concurrent。
谢谢你的帮助 !
解决方案
保持加载线程的句柄以查看其是否正在运行?
还是不能仅使用同步标志来指示负载是否在进行中?
我来自.net背景(完全没有Java经验),但是我们可以尝试某种简单的静态标志,该标志在方法开始时会检查其运行是否正常。然后,我们需要做的就是确保对该标志的任何读/写都已同步。因此,在开始时检查标志(如果未设置),将其设置(如果设置),然后返回。如果未设置,请运行该方法的其余部分,并在完成后取消设置。只需确保将代码放入try / final中,并将标志iunsetting置于finally中,以便在发生错误的情况下始终保持未设置状态。非常简化,但可能仅此而已。
编辑:这实际上可能比同步方法更好。因为我们真的真的需要在翻译完成之后立即进行一次新翻译吗?而且,如果必须等待一段时间,我们可能不想将线程锁定太长时间。
实际上,这与完成经典方式时管理Singleton(gasp!)构造所需的代码相同:
if (instance == null) { synchronized { if (instance == null) { instance = new SomeClass(); } } }
内部测试与外部测试相同。外部测试是为了使我们不必常规地输入同步块,而内部测试是为了确认自上次进行测试以来情况没有更改(在进入"同步"之前,该线程可能已被抢占)。
在情况下:
if (translationsNeedLoading()) { synchronized { if (translationsNeedLoading()) { loadTranslations(); } } }
更新:这种构造单例的方法在JDK1.4下无法可靠地工作。有关说明,请参见此处。但是我认为我们在这种情况下会没事的。
使用某种形式的锁定机制仅在尚未执行任务时执行它。获取锁定令牌必须是一个一步的过程。看:
/** * @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(); } } }