Использование Объекта Dataoutputstream В Порожденном Классе (thread)

Nodit

New Member
27.01.2014
1
0
#1
Добрый день.
Есть код, порождающий класс и потом пытающийся использовать объекты порожденного класса.
Код:
	//Класс потока
class ClntMove extends Thread
{
public DataInputStream in;
public DataOutputStream out;
String ServerIp;
int ServerPort;
Socket clientSocket=null;
public boolean wolk = true;
static String inMessage;
public ClntMove(String Ip, int Port)
{ServerIp = Ip;
ServerPort = Port;
this.start();
}

public void run ()
{String Message;
try {
clientSocket = new Socket(ServerIp,ServerPort);
in = new DataInputStream(clientSocket.getInputStream());
out = new DataOutputStream(clientSocket.getOutputStream());
Message="testMSG";
out.writeUTF(Message);
Message=in.readUTF();
while (!clientSocket.isClosed())
{while(wolk)
{
...рабочий исполняемый код
}
}
clientSocket.close();
}
catch (UnknownHostException ex) {Logger.getLogger(ClntMove.class.getName()).log(Level.SEVERE, null, ex);}
catch (IOException | InterruptedException ex) {Logger.getLogger(ClntMove.class.getName()).log(Level.SEVERE, null, ex);}
}
}


//Главный класс
class ClientMLSP
{static ClntMove Move;
static boolean live = true;
static boolean event = false;


static void SelectPoint(ClntMove aMove)
{boolean temp=true;
try {
String Message="ClientMSG";
aMove.out.writeUTF(Message);[b]В оригинальном скрипте строка 142[/b]
Message=aMove.in.readUTF();
System.out.println(Message);
}
catch (UnknownHostException ex) {Logger.getLogger(ClientMLSP.class.getName()).log(Level.SEVERE, null, ex);}
catch (IOException ex) {Logger.getLogger(ClientMLSP.class.getName()).log(Level.SEVERE, null, ex);}
catch (InterruptedException ex) {Logger.getLogger(ClientMLSP.class.getName()).log(Level.SEVERE, null, ex);}

}

//Главный блок
public static void main (String[] args) throws InterruptedException
{String ServerIp="127.0.0.1"; //IP сервера
int SPort_Move=50000; //Порт для связи с сервером
Move=new ClntMove(ServerIp,SPort_Move);
while (live)
{
while (!event)
{SelectPoint(Move);[b]В оригинальном скрипте строка 183[/b]
}
}
}
}

В двух слова. В классе ClientMLSP создается объект класса ClntMove. Move=new ClntMove(ServerIp,SPort_Move);
ClntMove - независимый поток, который исправно выполняет возложенные на него функции обмена с сервером данными. Там все отлично работает.
после создания объекта Move я из класса ClntMlsp пытаюсь воспользоваться открытыми сокетными соединениями с сервером и получить оттуда некоторые данные. Для этого используется процедура ClntMLSP.SelectPoint и в нее, если я правильно понял материалы статей, передается ссылка на объект Move, созданные чуть ранее.
Внутри процедуры пытаюсь передать тестовые данные серверу
String Message="ClientMSG";
aMove.out.writeUTF(Message);
и получаю ошибку.
Exception in thread "main" java.lang.NullPointerException
at ClntBody.ClientMLSP.SelectPoint(ClientMLSP.java:142)
at ClntBody.ClientMLSP.main(ClientMLSP.java:183)

Про прогоне кода в режиме отладки ошибка не возникает. Как понимаю, при штатном запуске в SelectPoint я пытаюсь использовать еще не активированный DataOutputStream, который успевает активироваться при прогоне кода в режиме отладки.
Вопрос - каким образом можно дождаться активации DataOutputStream в параллельно выполняемом потоке и, в идеале, временно перехватывать контроль над ним, чтобы избежать возможности одновременной попытки загрузки в него данных разными потоками?
 

LuMee

Well-Known Member
02.05.2006
477
0
#2
Идея сделать out и in в ClntMove крайне неудачна: при таком подходе о нормальной синхронизации можно забыть.

Поэтому первым шагом будет сделать эти поля закрытыми (private или protected), а в ClntMove добавить публичные методы, через которые эти потоки можно читать/писать:
Код:
private DataInputStream in = null;
private DataOutputStream out = null;

public String read() {
return in.readUTF();
}

public void write(String msg) {
out.writeUTF(msg);
}
Следующим шагом будет обеспечение синхронизации: нельзя давать работать с потоками ввода/вывода до их инициализации. Тут можно использовать такой подход:
1. завести некий объект-монитор для синхронизации;
2. при попытке получить доступ к стриму блокировать текущий поток через объект-монитор, если стрим еще не инициализирован;
3. после инициализации стримов оповещать всех через монитор, что можно писать/читать.
В коде это будет выглядеть примерно так (все в классе ClntMove):
Код:
private final Object lock = new Object();

public void run() {
synchronized(lock) {
clientSocket = new Socket(ServerIp, ServerPort);
in = new DataInputStream(clientSocket.getInputStream());
out = new DataOutputStream(clientSocket.getOutputStream());

// Оповещаем ожидающие потоки, что стримы готовы к употреблению
lock.notifyAll();
}

// Ну и далее, что там требуется сделать
}

public void write(String msg) {
synchronized(lock) {
// Ждем готовности стримов
while(out == null) {
try {
lock.wait();
} catch(InterruptedException) { 
// Поток был прерван, пока ждал. Обработать по вкусу.
}
}

out.write(msg);
}
}
Добавлено: Разумеется, этот код - лишь демонстрация концепции, его еще надо доработать. В частности, надо продумать, как поступать в следущих ситуациях:
1. Сокет открыть не удалось, notifyAll не вызвался, в итоге метод write блокируется навсегда. Можно либо предусмотреть какие-то таймауты, либо флаги успешной/неуспешной инициализации и т.п.
2. Что делать, если поток был прерван (interrupt()) во время ожидания доступности стрима: выйти из метода, еще подождать и т.п.
3. Что-то еще, что мне сейчас с ходу не пришло в голову, но может стать проблемой :)