【问题标题】:What is the proper way to interrupt a client thread while it's calling an EJB method?在调用 EJB 方法时中断客户端线程的正确方法是什么?
【发布时间】:2011-03-08 10:37:20
【问题描述】:

我有一个 java 客户端,它在新线程中管理其服务器调用以防止 GUI 冻结。

即使在许多地方都阻止了这种情况,也可能会在同一模型上再次调用该方法,例如使用不同的参数。在这种情况下,我显然希望具有最新参数的最新调用成为“成功”并显示其结果的调用。

我有一个系统跟踪先前启动的线程,并在启动新线程之前中断它(Thread.interrupt())。然后其他方法在将新结果发送到 GUI 元素之前检查它们是否在非中断线程中运行(使用 if (Thread.currentThread().isInterrupted())。

这个结构与以前的服务器连接器一起工作,因为我是唯一一个检查进程中断标志的人。我的问题是我现在在客户端中使用 EJB 方法调用,它们对中断的线程反应很糟糕。在 EJB 调用期间中断线程将触发 RuntimeException,其中包括 InterruptedException。而且这似乎不是一件正常的事情。

显然我可以在每个服务器调用中捕获 RuntimeExceptions 并检查它们的中断原因,但它似乎不是很“干净”。

我的问题是:在这种情况下我能做什么?中断运行 EJB 方法调用的线程的正确方法是什么?

【问题讨论】:

  • 完成对 EJB 的新过时调用是否重要?还是您真的需要停止 EJB 端的处理?
  • @Rich - 这并不重要。停止它会更好,但很可能需要在服务器端有一个更大的结构,包括消息传递。对于这个问题,我只关注客户端。

标签: java multithreading ejb interrupted-exception


【解决方案1】:

既然您不介意在服务器上继续过时的 EJB 调用,为什么不让调用线程“自然地”终止,而是丢弃结果,因为它的调用已被另一个线程取代?我没有时间提供示例实现,但您可能会发现使用 Futures 和相关的 Java 并发类有所了解。

编辑

除此之外,您可能会发现类似这样的方法可以解决问题,但我觉得它很老套,我相信还有更优雅的解决方案。

在调用线程上(可能是按钮的onclick方法):

AsynchronousResultManager.registerRequest("UNIQUE_IDENTIFIER", runnableExecuteRequest);

registerRequest 会执行以下操作:

registerClick(String id, Runnable execution) {
    AtomicReference ref = executions.get(id); //executions is a Map<String, AtomicReference> created as a a computing map by Guava MapMaker
    execution.setReference(ref); //so that the Runnable has a reference to it later
    ref.set(execution); //this will overwrite an existing reference to a previous invocation.
    //here you need to actually kick off your thread in whatever way works best for you
}

执行请求的runnable 将是以下的子类:

public abstract class RequestRunnable implements Runnable {

    private AtomicReference ref;

    public void run() {
        doRunInternal(); //actually go off and do the request to the J2EE server
        if (this == ref.get()) { //ie if the current runnable is the same as in the reference, we can proceed to actually dispatch the result
            dispatchResult(); //this method would do something like add a runnable to the SwingWorkerThread
        }
    }

    protected abstract void doRunInternal();
    protected abstract void dispatchResult();

    public void setReference(AtomicReference ref) {
        this.ref = ref;
    }

}

这可能会崩溃和烧毁,但希望它可以引导您进行查询...

【讨论】:

  • 感谢您的广泛回答。未来(和并发包)可能是要走的路,但当前系统在程序中使用得太广泛,无法重构所有它,因为它是。我同意这将是处理此问题的最佳方式。
  • 关于第二个例子,我当前的实现实际上是一个处理线程的抽象类,而实际工作是在一个由我的模型实现的抽象方法中。我的问题是我的子模型不依赖于相同的框架,并且它们自己处理“dispatchResult”部分。因此,它也需要一些重构才能走这条路(尽管比第一个解决方案要少)。
【解决方案2】:

要停止线程,您需要执行 2 种操作:

  • 如果线程正在等待阻塞操作(IO、网络、锁...),您需要中断它。将引发 InterruptedException,使运行的代码有机会捕获异常并以适当的方式停止。
  • 如果线程只是做一些处理,Thread.interrupt() 将无济于事。不会抛出异常,线程将继续处理它。处理代码需要定期检查您是否仍希望进程继续进行。

无论如何,要正确执行此操作,您需要由您要停止的线程运行的代码处理这两种情况。这里没有灵丹妙药。

【讨论】:

  • 我已经介绍了第二种情况,因为我正在检查线程是否中断的每个繁重处理之间,以及在服务器调用之前/之后。然而,对于第一点,调用中断会从 EJB 调用中触发 RuntimeException。我的问题并不是真的知道我什么时候被打断,我已经在管理它并停止对线程的任何进一步处理。我的问题确实在于 EJB 抛出的“新”异常。
【解决方案3】:

鉴于现有的架构,我最终从每个服务器调用中捕获了 RuntimeException,形式如下:

try {
    return getEJBService().getServiceResult(param, param, param);
} catch (RuntimeException e) {
    Throwable cause = e.getCause();
    if (cause instanceof InterruptedException)
        throw (InterruptedException)cause;
    else
        throw e;
}

它不是很漂亮,但至少它可以让我根据我当前模型的中断来行动。

在理想的世界中,人们宁愿选择Rich 给出的one of the solutions

【讨论】:

    猜你喜欢
    • 2015-09-13
    • 1970-01-01
    • 1970-01-01
    • 2021-02-06
    • 1970-01-01
    • 2021-02-03
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    相关资源
    最近更新 更多