我有一台服务器接受具有stop()
方法的客户端关闭服务器,导致我想要解决的java.nio.AsynchronousCloseException
。在不同的线程上调用stop()
方法,这是导致我相信的竞争条件的原因。
这是我的代码:
public void run() {
InetSocketAddress addr = new InetSocketAddress(provider.getConnection(), 12354);
try {
server = ServerSocketChannel.open();
server.configureBlocking(true);
server.socket().bind(addr);
parent.setIP(addr.getAddress().getHostAddress().toString());
password = generatePassword();
parent.setPassword(password);
parent.setStatus("Ready.");
} catch (IOException e) {
parent.die("Could not start server: " + e.getMessage());
runner = null;
}
while (runner == Thread.currentThread()) {
try {
SocketChannel sc = server.accept();
if (available) {
session = new ReceiveSession(this, sc, password, addr.getAddress());
session.start();
available = false;
} else {
new ReceiveBusyHandler(sc).start();
}
} catch (IOException e) {
synchronized (swallowException) {
if (!swallowException) {
parent.showError(e.toString());
}
available = true;
}
}
}
}
public void stop() throws IOException {
synchronized (swallowException) {
swallowException = true;
runner = null;
if (server != null) {
server.socket().close();
server.close();
}
swallowException = false;
System.out.println("Server down");
}
}
(仅供参考,swallowException
是Boolean
,你可以看到我尝试过同步它。)
看起来stop()
方法正在将swallowException
设置为true
,然后在我的服务器循环中的异常处理程序有机会访问它之前返回false
。
更新:我介绍了一个新的Object
用作锁定,并使用wait()/notify()
来解决我的问题:
public void run() {
InetSocketAddress addr = new InetSocketAddress(provider.getConnection(), 12354);
try {
server = ServerSocketChannel.open();
server.configureBlocking(true);
server.socket().bind(addr);
parent.setIP(addr.getAddress().getHostAddress().toString());
password = generatePassword();
parent.setPassword(password);
parent.setStatus("Ready.");
} catch (IOException e) {
parent.die("Could not start server: " + e.getMessage());
runner = null;
}
while (runner == Thread.currentThread()) {
try {
SocketChannel sc = server.accept();
if (available) {
session = new ReceiveSession(this, sc, password, addr.getAddress());
session.start();
available = false;
} else {
new ReceiveBusyHandler(sc).start();
}
} catch (IOException e) {
synchronized (lock) {
if (!swallowException) {
parent.showError(e.toString());
}
lock.notify();
available = true;
}
}
}
}
public void stop() throws IOException {
synchronized (lock) {
swallowException = true;
runner = null;
if (server != null) {
server.socket().close();
server.close();
}
while (swallowException) {
try {
lock.wait();
swallowException = false;
} catch (InterruptedException e) {
}
}
//swallowException = false;
System.out.println("Server down");
}
}
答案 0 :(得分:1)
此部分未正确同步:
synchronized (swallowException) {
swallowException = true;
您正在同步一个实例(false
)并立即更改swallowException
引用以指向其他实例(true
)。输入stop
的下一个帖子不会阻止。
要么在不会被换出的实例上同步(这些方法的所有者),要么使用java.util.concurrent
中的其他锁定机制。
答案 1 :(得分:1)
在Java中,同步是在对象上完成的,而不是在变量上完成的。在swallowException
上同步时,您会同步其值(Boolean.TRUE
或Boolean.FALSE
)。这不是你想要的。您应该同步包含swallowException
的对象。
答案 2 :(得分:0)
我强烈建议您重构代码(甚至是您作为更新发布的解决方案),因为它不清楚发生了什么。
从您的说明,您似乎只想要一种线程安全的方法来停止您的服务器。我建议您这样做,只需call close() on the ServerSocket,就可以抓住SocketException
:
private boolean cont = true;
// this can safely be called from any thread
public synchronized void stop() {
cont = false;
if (server != null) {
server.socket().close();
}
}
private synchronized void setContinue(boolean value) {
cont = value;
}
private synchronized boolean shouldContinue() {
return cont;
}
private synchronized void openChannel() {
server = ServerSocketChannel.open();
}
public void run() {
InetSocketAddress addr = new InetSocketAddress(provider.getConnection(), 12354);
try {
openChannel();
server.configureBlocking(true);
server.socket().bind(addr);
parent.setIP(addr.getAddress().getHostAddress().toString());
password = generatePassword();
parent.setPassword(password);
parent.setStatus("Ready.");
} catch (IOException e) {
parent.die("Could not start server: " + e.getMessage());
setContinue(false);
}
while (shouldContinue()) {
try {
SocketChannel sc = server.accept();
if (shouldContinue()) {
if (available) {
session = new ReceiveSession(this, sc, password, addr.getAddress());
session.start();
available = false;
} else {
new ReceiveBusyHandler(sc).start();
}
}
} catch (SocketException se) {
// normal shutdown from stop()
} catch (IOException e) {
parent.showError(e.toString());
available = true;
}
}
System.out.println("Server down");
}