Lock Thread

Tenho esta classe de um cliente socket que recebe e envia objetos, o problema é que posso receber um objeto a qualquer momento, e posso enviar a qualquer momento, não existe uma ordem.

Eu sei que o lock é na leitura do get() com o socket, mas não consigo pensar em outra lógica para poder enviar e receber a qualquer momento sem que um bloqueie o outro.

public class Conexao implements Runnable {
    
    private String dst;
    private int porta;
    
    private Socket socket;
    
    private boolean connected;
    private Thread listenerInputMessage;
    
    private ControleCliente controle;
       
    
    /** Creates a new instance of Conexao */
    public Conexao(ControleCliente controle, String dst, int porta) {
        super();
        
        this.controle = controle;
        
        this.dst = dst;
        this.porta = porta;
        
        socket = null;
        
        //connect();
    }
    
    private boolean connect() {
        try {
            socket = new Socket(dst, porta);
            
            setConnected(true);
            
            return true;
            
        } catch (UnknownHostException ex) {            
            ex.printStackTrace();          
            return false;
        } catch (IOException ex) {            
            ex.printStackTrace();
            return false;
        }
    }
    
    public void run() {
        while(connected) {
            if (getSocket() != null) {
                get();
            }
        }
    }
    
    public synchronized void get() {
        try {
            
            ObjectInputStream oi = new ObjectInputStream(getSocket().getInputStream());
            
            controle.comReceived((Comunicacao) oi.readObject());
            
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
    
    public synchronized void send(Comunicacao pacote) {
        if (!isConnected()) {
            connect();
        }       
        
        try {
            ObjectOutputStream oo = new ObjectOutputStream(getSocket().getOutputStream());
            
            oo.writeObject(pacote);            
        } catch (IOException ex) {
            System.out.println("Deu erro no envio da classe"); //debug
            ex.printStackTrace();            
        }
    }

    public synchronized boolean isConnected() {
        return connected;
    }

    public synchronized void setConnected(boolean connected) {
        this.connected = connected;
        
        if (connected) {
            listenerInputMessage = new Thread(this);
            listenerInputMessage.start();
        }
        else {
            listenerInputMessage.interrupt();
            listenerInputMessage = null;
            
            try {                
                getSocket().close();                
                
                setSocket(null);
                
                System.out.println("Desconectado");
            }
            catch(IOException ioe) {
                ioe.printStackTrace();
            }
        }
    }
    
    public synchronized Socket getSocket() {
        return socket;
    }
    
    public synchronized void setSocket(Socket socket) {
        this.socket = socket;
    }
}

que tipo de ordem vc imaginou?

Funciona assim,

Quando eu vou enviar o primeiro objeto, eu faço a conexão e chamo o método setConnected(boolean connected).

Ao chamar este método eu inicio uma thread onde tenho um loop que fica executando até receber algo.

Mas eu tenho o método send que pode a qualquer momento tentar enviar um objeto.

Mas como eu tenho método get() recebendo no socket ele dá um lock, e não consigo pensar em outra implementação para resolver o problema de envio/recebimento sem uma ordem. E não consigo resolver o problema do lock.

Crie objetos diferentes para serem usados como monitores na sincronização:

[code]public class Conexao implements Runnable {
private int[] inputLock = new int[0];
private int[] outputLock = new int[0];

public void get() {
synchronized (inputLock) {
//seu código aqui.
}
}

public void read() {
synchronized (outputLock) {
//seu código aqui
}
}
}[/code]

Só não esqueça que dentro desses blocos sincronizados, você deve dar wait e notify nos objetos monitores, não diretamente.

Um método synchronized sem um objeto monitor é equivalente a synchronized(this). Uma escolha infeliz, já que isso também pode ser uma falha de encapsulamento.

Outro comentário. Dê também uma olhada na classe Lock do pacote java.util.concurrent.

Outra alternativa é você ter uma classe de Conexão e duas inner classes, cada uma implementando Runnable e controlando o envio/recepção de dados (basicamente, uma thread controla o input e a outra o output stream do socket)

Se eu fosse implementar (e eu já fiz isso) faria dessa forma.

Então rapaz… o problema foi exatamente o meu exagero com os métodos sincronizados.

Eu estava pensando errado quanto a sincronização do java. Acreditava que o lock era à nível de método (sei lá de onde tirei isto) e quando na verdade ele bloqueia o objeto inteiro.

Então como eu tenho aquele loop lendo o método getSocket() ele ficava lá parado esperando a chegada de um objeto. E como é sincronizado ele bloqueava todo o objeto.

Por isto eu não conseguia acessar outro método do objeto.

Resolvi o problema retirando o excesso de synchronize, resolvi fazer o que devia ter feito, uma análise de onde realmente é necessário. Além de resolver o meu problema ainda vou melhorar a performance do meu programa.

Valew pessoal.

Engraçado, aquela classe não me parece ter synchronized em excesso.