Я довольно новичок в Java в целом и в параллельном программировании, в частности, так что простите меня, если это вопрос новичков.Синхронизация между родительским и дочерним потоком (дочерний поток «на мониторе»)
У меня есть поток (сервер), который управляет коллекцией дочерних потоков (каждый поток представляет собой сеанс между клиентом и сервером). Сервер поддерживает набор сеансов, и когда сеанс заканчивается, он сигнализирует о завершении сеанса родительского сервера, чтобы сервер мог удалить его из коллекции сеансов.
Мне сказали, что ArrayLists необходимо защищать, если вы намерены использовать их с потоками, а ints также могут иметь проблемы, если не синхронизированы, поэтому методы, которые используют оба, синхронизируются.
Далее следуют соответствующие части сервера и объекты сеанса.
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);
}
}
Это класс Session:
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. Однако я не мог найти объяснения, почему дочерний поток, вызывающий синхронизированный метод в родительском классе, вызовет поток, видимо, постоянно находящийся в этом состоянии или что с ним делать.
Я обнаружил, что если метод cleanupSession в классе Server не помечен как синхронизированный, то потоки прекращаются, как я ожидал от них. Однако, если мне нужно синхронизировать, чтобы поддерживать безопасность потоков, я не могу просто оставить метод несинхронизированным и доверять удаче.
У меня явно нет чего-то основного, но я не уверен, что. Если кто-нибудь может указать, что я здесь делаю, я буду признателен.
(Добавление: Я ожидаю, что есть другой класс Collection, который я должен использовать, а не ArrayList, и, зная, что это будет, несомненно, будет здорово в решении этого конкретного случая, но мне также хотелось бы получить обратную связь о том, как избежать эта проблема в общем случае, когда единственной доступной опцией является синхронизация)
Спасибо. Я изучил несколько модулей Java во время учебы, но это первый материал, который я сделал с параллелизмом. Ясно, что мне еще многое предстоит узнать. – GordonM
Кроме того, как я думаю, я прочитал в «Чистом коде», ints необходимо синхронизировать или заменить AtomicInteger, поэтому я переключил код на использование AtomicInteger. – GordonM
@ GordonM в вашем коде есть только три 'ints': на сервере,' listenPort' и 'lastId' доступны только из одного потока (тот, который выполняет метод Server.run()'), t необходимо синхронизировать. То же самое относится к единственному «int» в клиенте. Вам нужна синхронизация только тогда, когда к некоторым данным можно получить доступ более чем к одному потоку в течение его жизни. –