Rox Java NIO Tutorial有一个示例 NIO Server 类。我不相信该代码实际上是线程安全的。以下代码由某个用户类调用以通过服务器发送数据:
public void send(SocketChannel socket, byte[] data) {
synchronized (this.pendingChanges) {
this.pendingChanges.add(new
ChangeRequest(socket, ChangeRequest.CHANGEOPS, SelectionKey.OP_WRITE));
synchronized (this.pendingData) {
List queue = (List) this.pendingData.get(socket);
if (queue == null) {
queue = new ArrayList();
this.pendingData.put(socket, queue);
}
queue.add(ByteBuffer.wrap(data));
}
}
this.selector.wakeup();
}
这是他们用于 run() 方法的相关代码:
while (true) {
try {
synchronized (this.pendingChanges) {
Iterator changes = this.pendingChanges.iterator();
while (changes.hasNext()) {
ChangeRequest change = (ChangeRequest) changes.next();
switch (change.type) {
case ChangeRequest.CHANGEOPS:
SelectionKey key = change.socket.keyFor(this.selector);
key.interestOps(change.ops);
}
}
this.pendingChanges.clear();
}
this.selector.select();
...
}
}
我的问题是,如果在主循环期间发生上下文切换,在同步块(synchronized block)之后但在调用 select() 之前调用公共(public)发送方法,那么有可能只保留写入直到下一个数据已写入或准备好读取(选择器键不会更新为写入,并且选择器不会接收唤醒信号)。就我的目的而言,这是 Not Acceptable (如果属实),但我不能仅仅为了适应这种边缘情况而对功能进行太多更改(例如为选择添加超时)。我试图想出一种方法来更好地同步各个部分,或者确保在发生上下文切换之前调用 select,但我陷入了困境。
编辑:由于对并发问题有些困惑,我将在这里更清楚地说明它。
运行线程:进入同步块(synchronized block),没有挂起的更改。
运行线程:退出同步块(synchronized block)。
调度程序:上下文切换。
其他线程:调用send方法。
其他线程:将挂起的更改和挂起的数据排入队列
其他线程:调用selector.wakeup()
调度程序:上下文切换。
运行线程:在selector.select()上阻塞,忽略待处理的数据。
运行线程:如果没有人尝试再次使用套接字,则继续永远阻塞。
我认为这涉及 NIO 和多线程的原因是因为我正在寻找一种正确同步 selector.select() 方法的方法。
最佳答案
the selector wouldn't receive the wakeup signal
和
then trying to wake up the selector (which does nothing because the selector isn't being selected on)
不真实。请参阅Javadoc :
If no selection operation is currently in progress then the next invocation of one of these methods will return immediately unless the
selectNow()
method is invoked in the meantime.
关于java - Rox 教程中的 Java NIOServer 真的安全吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39087029/