是否有更快版本的SynchronousQueue只能同步2个线程?

时间:2010-12-17 03:51:41

标签: java multithreading

我的应用程序中有一个案例,我需要启动异步任务,然后阻止它完成。 (是的,是的,我知道它不是最优的,但它是我们正在使用的库的工件。)

具体来说,我需要调用一个函数并传递一个回调对象,实际的回调将在一个单独的线程上进行。但我想等待回调发生,实质上是在异步机制之上构建同步机制。

所以我的代码看起来像这样:

private static class MethodCallback implements RpcCallback<Message> {
    private Message message = null;
    private boolean done = false;
    private long started = System.currentTimeMillis();

    public synchronized void run(Message parameter) {
        long elapsed = System.currentTimeMillis() - started;
        log.debug("Got callback after {} millis", elapsed);
        this.message = parameter;
        this.done = true;
        notifyAll();
    }

    public synchronized void await() throws ServiceException {
        while(!done && (System.currentTimeMillis() - started < MAX_WAIT_MILLIS)) {
            try {
                long remaining = (started + MAX_WAIT_MILLIS) - System.currentTimeMillis();
                if(remaining <= 0) {
                    remaining = 1;
                }
                wait(remaining);
            } catch(InterruptedException e) {
                break;
            }
        }
        if(!done) {
            String msg = String.format("Timeout: No response from async process");
            log.warn(msg);
            throw new ServiceException(msg);
        }
    }

    public Message get() {
        return message;
    }
}

public Message getMessageFromAsyncProcess() {
    MethodCallback callback = new MethodCallback();
    channel.doAsyncThing(callback);
    callback.await();
    Message result = callback.get();
    if(result == null) {
        throw new ServiceException("Error: async process did not produce a result");
    }
    return result;
}

现在,此代码运行良好,应用程序可以快速执行。但是,分析确定MessageCallback.run是性能瓶颈,可能是由于同步。

因此,作为一项实验,我修改了代码以使用SynchronousQueue代替,期望这会提高性能。但令我惊讶的是,它的运行速度慢了两倍(平均每次呼叫大约15毫秒,而每次通话大约需要7毫秒)。

我可能会使用Semaphore或类似AtomicBoolean之类的东西来破解替代机制,但我是否应该预期其中任何一种机制会更好?

鉴于我只有两个线程,而我只是想让其中一个线程等到另一个产生一个结果,那么有没有比我现在做的更好的机制?

2 个答案:

答案 0 :(得分:2)

我猜您使用的是时钟分辨率约为15 ms的Windows系统。我建议您尝试使用System.nanoTime()来获得更准确的时间。

最明显的解决方案是使用执行程序服务,该服务只需要几行代码就可以完全按照您的目标实现。

你应该能够在传递任务的线程之间来回切换,大约1 - 8 us (micro-seconds)即使在负载下,你最好的时间也应该是这个时间。

注意:您的wait()方法在获得锁定之前不会被唤醒(run()完成后)这可能不是您想要的。

答案 1 :(得分:1)

看一下代码,我看不到任何明显会导致它成为瓶颈的问题。

如果你有7ms和15ms这样的时间,我怀疑根本原因是你有比物理处理器更多的活动(可运行)线程。如果是这种情况,那么长时间的延迟是由于等待处理器可用于运行已经变为可运行的线程。您可以通过减少线程数来改善平均延迟,但这也可能会降低整体吞吐量。

顺便说一下,我认为您的自定义同步代码可以替换为使用FutureTask,但我不认为这会减少平均通话时间。