我有一个接受客户机的服务器,它有一个关闭服务器的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,你可以看到我尝试过同步它。)
看起来,在我的服务器循环中的异常处理程序有机会访问swallowException之前,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");
}
}发布于 2013-04-28 18:40:03
在Java中,同步是在对象上完成的,而不是在变量上完成的。在swallowException上同步时,将根据其值(Boolean.TRUE或Boolean.FALSE)进行同步。这不是你想要的。您应该在包含swallowException的对象上进行同步。
发布于 2013-04-28 18:39:35
此部件未正确同步:
synchronized (swallowException) {
swallowException = true;您正在一个实例(false)上同步,并立即将swallowException引用更改为指向另一个实例(true)。进入stop的下一个线程不会阻塞。
要么在不会被换出的实例上进行同步(这些方法的所有者),要么使用java.util.concurrent中的其他锁定机制。
发布于 2013-04-28 18:56:50
我强烈建议重构你的代码(甚至是你作为更新发布的解决方案),因为它只是不清楚发生了什么。
从您的描述来看,您似乎只是想要一个线程安全的方式来停止您的服务器。我建议这样做,这样你就可以简单地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");
}https://stackoverflow.com/questions/16261974
复制相似问题