我对 Java 尤其是并发编程相当陌生,所以如果这是一个新手问题,请原谅我。
我有一个线程(服务器),它管理一组子线程(每个线程代表客户端和服务器之间的会话)。服务器维护一个会话集合,当会话结束时,它会向父服务器发出它已完成的信号,以便服务器可以将其从会话集合中删除。
有人告诉我,如果您打算将 ArrayLists 与线程一起使用,则需要保护它们,并且除非同步,否则 int 也可能存在问题,因此使用两者的方法是同步的。
服务器和会话对象的相关部分如下。
public class Server {
private int listenPort = 0;
private ServerSocket serverSocket = null;
private List<Session> sessions = new ArrayList ();
private int lastId = 0;
/**
* Start listening for clients to process
*
* @throws IOException
* @todo Maintain a collection of Clients so we can send global messages
* @todo Provide an escape condition for the loop
*/
synchronized public void run () throws IOException {
Session newSession;
// Client listen loop
while (true) {
//int sessionId = this.Sessions.
newSession = this.initSession (++this.lastId);
this.sessions.add (newSession);
//this.Sessions.add (newSession);
new Thread (newSession).start ();
}
}
/**
*
* @return
* @throws IOException
*/
public Socket accept () throws IOException {
return this.getSocket().accept ();
}
/**
*
* @param closedSession
*/
synchronized public void cleanupSession (Session closedSession) {
this.sessions.remove (closedSession);
}
}
这是会话类:
public class Session implements Runnable {
private Socket clientSocket = null;
private Server server = null;
private int sessionId = 0;
/**
* Run the session input/output loop
*/
@Override
public void run () {
CharSequence inputBuffer, outputBuffer;
BufferedReader inReader;
try {
this.sendMessageToClient ("Hello, you are client " + this.sessionId);
inReader = new BufferedReader (new InputStreamReader (this.clientSocket.getInputStream (), "UTF8"));
do {
// Parse whatever was in the input buffer
inputBuffer = this.requestParser.parseRequest (inReader);
System.out.println ("Input message was: " + inputBuffer);
// Generate a response for the input
outputBuffer = this.responder.respond (inputBuffer);
System.out.println ("Output message will be: " + outputBuffer);
// Output to client
this.sendMessageToClient (outputBuffer.toString ());
} while (!"QUIT".equals (inputBuffer.toString ()));
} catch (IOException e) {
Logger.getLogger (Session.class.getName ()).log (Level.SEVERE, null, e);
} finally {
this.cleanupClient ();
}
}
/**
* Terminate the client connection
*/
public void cleanupClient () {
try {
this.streamWriter = null;
this.clientSocket.close ();
this.server.cleanupSession (this);
} catch (IOException e) {
Logger.getLogger (Session.class.getName ()).log (Level.SEVERE, null, e);
}
}
/**
*
* @param clientSocket
*/
public Session (Server owner, int sessionId) throws IOException {
System.out.println ("Class " + this.getClass () + " created");
this.server = owner;
this.sessionId = sessionId;
this.clientSocket = this.server.accept ();
System.out.println ("Session ID is " + this.sessionId);
}
}
我遇到的问题出在会话的 CleanupClient 方法中。当服务器中的 CleanupSession 方法标记为同步时,会话线程似乎不会终止。相反,根据 Netbeans 的说法,它们进入了一个名为“On Monitor”的状态。
我试图找出这意味着什么以及如何处理它并没有帮助。我确实发现监视器就像一个只能由单个线程占用的空间,其他线程必须等待轮到它才能使用它,这就是Java中实现并发的方式。但是,我找不到解释,为什么在父类中调用同步方法的子线程会触发线程明显永久地进入这种状态,或者如何处理它。
我确实发现,如果 Server 类中的 cleanupSession 方法没有标记为同步,那么线程会按照我的预期终止。但是,如果我需要同步以维护线程安全,那么我不能只让方法不同步并相信运气。
我显然缺少一些基本的东西,但我不确定是什么。如果有人能指出我在这里做错了什么,我将不胜感激。
(附录:我希望我应该使用其他一些 Collection 类而不是 ArrayList,并且知道它是什么对于解决这个特殊情况肯定会很好,但我也想在如何避免这个问题的反馈一般情况下,唯一可用的选项是同步)