假设一个应用程序在一个单独的线程中启动服务器。稍后,服务器将从另一个线程接收停止命令。
我在这个实现中看到的第一个问题是whole Server.stop()方法是同步的。官方的Java文档说,“从同步代码调用其他对象的方法会导致活性问题”(来源:http://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html)。
第二个问题是,线程-2调用Server.stop()方法线程-1(服务器)的时刻可能位于ServerSocket.accept()方法。这意味着ServerSocket由两个线程同时访问。
这些问题真的会导致问题吗?还是下面的服务器代码完全没问题?
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Application {
public static void main(String...args){
// Thread-1 = server thread
Server server = new Server(1337);
new Thread(server).start();
// Thread-2 = any class stopping the server at some point
new Thread(new Runnable(){
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException ignore) {
}
// Call to questionable method
server.stop();
}
}).start();
}
}
class Server implements Runnable {
protected int port;
protected ServerSocket serverSocket;
protected boolean running;
public Server(int port) {
this.port = port;
this.serverSocket = null;
this.running = true;
}
public void run() {
try {
this.serverSocket = new ServerSocket(this.port);
} catch (IOException e) {
System.out.println("Server can not be started. " + e.getMessage());
}
while (this.isRunning()) {
Socket socket = null;
try {
// 2. Server thread at blocking accept method
socket = this.serverSocket.accept();
} catch (IOException e) {
if (!this.isRunning()) {
System.out.println("Server stopped.");
return;
}
}
// Do something with the socket
System.out.println("Client connected: " + socket.getInetAddress());
}
System.out.println("Server terminated.");
}
private synchronized boolean isRunning() {
return this.running;
}
public synchronized void stop() {
if (running) {
this.running = false;
try {
// 1. Nested synchronized method call
this.serverSocket.close();
} catch (IOException e) {
System.out.println("Error closing server socket.");
}
} else {
System.out.println("Server is already stopped.");
}
}
}服务器代码基于:http://tutorials.jenkov.com/java-multithreaded-servers/multithreaded-server.html
发布于 2015-04-23 13:07:23
“从同步代码调用其他对象的方法可能会造成活性问题”
他们警告您,当您编写一个线程一次可以锁定多个锁的代码时,如果您没有计划,那么您将面临死锁的风险。
典型的死锁场景涉及两个线程,A和B,以及两个锁1和2。
called_in_thread_A() {
synchronized(lock1) {
synchronized(lock2) {
doSomething();
}
}
}
called_in_thread_B() {
synchronized(lock2) {
synchronized(lock1) {
doSomething();
}
}
}如果同时调用这些方法,线程A可以获得锁1,而线程B同时获得锁2。在这一点上,两个线程都不能通过第二个锁,只要其他线程持有它,并且两个线程都不会释放它持有的锁,直到它通过第二个锁为止。两条线都不会有任何进展。
在这个例子中,问题是显而易见的,但是在一个大型复杂的程序中,死锁的可能性并不是那么容易看到。有避免死锁的策略,也有检测死锁的策略,在某些框架中,还有打破死锁的策略。
如果在线程一次锁定多个锁的大型复杂程序中没有使用任何这些策略,那么您的程序将面临死锁的风险。
服务器程序的一种策略是让同步方法排队等待工作线程执行任务。(例如,使用Swing框架的invokeLater(Runnable r)方法。)然后,工作线程可以安全地调用另一个对象的synchronized方法。
发布于 2015-04-24 07:56:45
第二个问题是,线程-2调用Server.stop()方法线程-1(服务器)的时刻可能位于ServerSocket.accept()方法。这意味着ServerSocket由两个线程同时访问。
在深入研究Java之后,我发现这正是ServerSocket应该工作的方式。
关闭这个插座。任何当前在accept()中被阻塞的线程都会抛出一个SocketException。 如果这个套接字有一个相关联的通道,那么通道也会关闭。
来源:https://docs.oracle.com/javase/8/docs/api/java/net/ServerSocket.html#close--
https://stackoverflow.com/questions/29820525
复制相似问题