2011-07-12 65 views
3

Javadoc AsynchronousByteChannel.read()表示该操作异步发生,但是在达到流尾时发生了什么?是否允许在调用read()的同一线程中触发完成处理程序?从实现的角度来看,没有理由异步执行此操作,因为我们已经知道结果。同样,如果用户试图读入剩余()返回0的ByteBuffer,我们知道读取操作必须返回0.AsynchronousByteChannel的线程影响

我问,因为我在自己的AsynchronousByteChannel实现中遇到竞态条件。我正在调用完成处理程序,该操作程序在操作完成时调用notify()。然后,我引用下面的用户代码:

CompletionHandler<?, ?> handler = ...; 
synchronized (handler) 
{ 
    asyncByteChannel.read(handler); 
    handler.wait(); 
} 

注意用户是假设在操作完成时的处理程序将被通报,但由于阅读()实际上调用完成处理程序同步它被等待前通知()后者将永远封锁。

规范是否要求我在单独的线程中更新CompletionHandler,或者用户是否应该意识到调用read()的线程可以同步执行某些操作的事实?

+1

它的线程模型是一团糟。异步组上的javadoc是不可解析的。线程应该留给开发人员 - 这很容易。不幸的是,他们决定帮助我们。结果非常复杂,不直观,没有记录(因为他们无法解释混乱)。一位偶然的评论者可能会发现API是正确的 - 实际上为什么它应该很难?但是当人们认真构建一个应用程序时,复杂性会让他变得疯狂。 – irreputable

回答

1

看着http://www.docjar.com/html/api/sun/nio/ch/AsynchronousSocketChannelImpl.java.html他们总是更新CompletionHandler在一个单独的线程,但未来更新在同一个线程。搜索变量hasSpaceToRead以查找有问题的方法。

我猜他们的推理路线是这样的:

  1. 我们创造未来返回给用户,所以没有办法,他与它(同步等)的交互,我们返回之前目的。
  2. 用户创建CompletionHandler,所以我们无法控制实现的功能(我们知道,我们可能会触发死锁!)。不要冒险,在单独的线程中释放完成处理程序。

UPDATE:我纠正了。根据Invoker.invoke()“如果当前线程在通道组的线程池中,则直接调用该处理程序,否则会间接调用该处理程序。”

解决:根据http://download.oracle.com/javase/7/docs/api/java/nio/channels/AsynchronousChannelGroup.html“凡I/O操作立即完成,并且发起线程是组,则完成处理程序可以由发起线程直接援引在池线程之一”

1

即使处理程序在另一个线程上调用,也不保证将在方法返回后(即在wait()启动后)调用。 (好吧,同步锁似乎保证这一点。)

您应该使用同步和等待,并锁定一个布尔变量:

CompletionHandler<?, ?> handler = ...; 
synchronized (handler) 
{ 
    asyncByteChannel.read(handler); 
    while(!handler.finished) { 
    handler.wait(); 
    } 
} 

...然后你的处理器将设置finished变量真正。

+0

如果**我被允许在主线程上处理CompletionHandler,那么您建议的解决方案非常棒。目前还不清楚是否是这种情况:) – Gili

+0

不,该解决方案始终可用。 (如果它不被允许,这是没有必要的 - 但是你应该等待一个循环,因为可能会有虚假的唤醒。) –

+0

我同意,但这不是这个问题。这是关于规范是否允许在同一个线程上通知。 – Gili