1. Este site usa cookies. Ao continuar a usar este site está a concordar com o nosso uso de cookies. Saber Mais.

Java Thread Synchronization

Discussão em 'Programação' iniciada por lilcrazy, 4 de Novembro de 2008. (Respostas: 7; Visualizações: 1522)

  1. lilcrazy

    lilcrazy Power Member

    Boas pessoal, estou neste momento a desenvolver em Java uma aplicação de Instant Messaging, do género messenger, só que utilizando o protocolo XMPP, ou seja, enviando mensagens XML de um cliente para um servidor e vice-versa (ver mensagens em RFC 3921: http://www.ietf.org/rfc/rfc3921.txt) [FONT=&quot]

    [/FONT]Já tenho, tanto o cliente como o servidor (multi-threaded, TCP) numa fase avançada de desenvolvimento. O meu maior problema está em sincronizar as threads que correspondem a cada cliente para, enquanto uma lê a informação de um ficheiro de objectos para carregar um vector de utilizadores para o servidor, um cliente ao registar-se a informação não fica guardada permanentemente, fica só temporariamente. Tanto nisto, como se por exemplo eu tiver mais que um cliente ligado, os dados não ficam guardados no ficheiro permanentemente apesar de eu ter o código para tal. Isto deve-se a problemas de sincronização na leitura e escrita de objectos no ficheiro. Cada método que tenho (registar, autenticar, devolverListaAmigos, adicioarAmigo, removerAmigo, mudarEstado, etc) criei como

    Código:
    public synchronized nomeMetodo(variaveis) throws exception {
    
    }
    Como posso resolver isto? Não estou a conseguir e já dei bastantes voltas... :/

    Obrigado pela ajuda
     
  2. xuxaki

    xuxaki Power Member

    Não sei se percebi bem... Mas o problema, basicamente, é guardar o Vector de utilizadores num ficheiro certo? E tens o método para escrever no ficheiro de objectos correcto? Podes postar aqui só esse método?
    É que para guardares essa informação a cada registo podes ter muitos acessos a disco... Uma sugestão seria teres uma Thread que de x em x tempo escrever o vector num ficheiro (o método, obviamente, teria de ser também synchronized).
     
  3. lilcrazy

    lilcrazy Power Member

    O meu problema não é escrever no ficheiro. Eu tenho um vector users que guarda objectos do tipo User (username, password, estado, vector de amigos). Consigo guardar isso num ficheiro e ler, até aí tudo bem.

    O problema é que tenho uma thread para cada cliente, e aí eu consigo fazer o login num, (leio e escrevo bem no ficheiro), mas com outro cliente ligado ao mesmo tempo (ou mais, cada um por thread), se eu quiser registar, apesar de guardar para ficheiro, perde esses dados logo de seguida...

    Não estou a conseguir sincronizar bem o acesso a esses dados por thread. Por cada thread eu tenho um método run(), e essa thread implements Runnable. No método run do servidor o que estou a fazer é receber os documentos XML e fazer o parsing desse documento. Se for para autenticar um utilizador chamo o método autentica, se for para registar chamo o método, e por aí em diante.

    Mas estou a chamar a esses métodos synchronized e não sei se é o mais correcto. Será o mais correcto dar o nome synchronized aos métodos de apenas leitura e escrita do vector? Dando um exemplo do registo:

    Código:
    public synchronized void register(Document doc, Element root) throws IOException {
    
                XMPPConnection session;
                
                Attribute from = root.getAttribute("from");
                username = from.getValue();
            
                Element pass = root.getChild("password");
                String password = pass.getValue();
            
                User user = new User();
                
                // see if it´s the first user to insert in database
                if(users.isEmpty() == true) {
             
                    // adds to vector, adds Object, saves to file
                    user.setUsername(username);
                    user.setPassword(password);
                    user.setUserStatus("offline");
                    Vector<Buddy> buddies = new Vector<Buddy>();
                    user.setBuddies(buddies);
                
                    users.add(user);
                    System.out.println(users.get(0).getUsername());
                    db.updateUsersList(users);
                    
                    session = new XMPPConnection();
                    
                    // register successful
                    String xmlMessage = "<iq type='register' to='"+username+"'><result>'success'</result></iq>";
    
                    doc = session.createXML(xmlMessage, "register_success");
        
                    session.sendMessage(doc, out);
                    
                    users = db.getRegisteredUsers();
    
                }
                else{
    
                    users = db.getRegisteredUsers();
                    
                    if(db.existsRegisteredUser(username, users) == false) {
                    
                        // adds to vector, adds Object, saves to file
                        user.setUsername(username);
                        user.setPassword(password);
                        user.setUserStatus("offline");
                        Vector<Buddy> buddies = new Vector<Buddy>();
                        user.setBuddies(buddies);
                
                        users.add(user);
                        db.updateUsersList(users);
                    
                        session = new XMPPConnection();
                    
                        // register successful
                        String xmlMessage = "<iq type='register' to='"+username+"'><result>'success'</result></iq>";
        
                        doc = session.createXML(xmlMessage, "register_success");
            
                        session.sendMessage(doc, out);
                        
                        users = db.getRegisteredUsers();
                
                    }else {
                    
                        session = new XMPPConnection();
                    
                        // register failed
                        String xmlMessage = "<iq type='register' to='" + username + "'><result>'failed'</result></iq>";
        
                        doc = session.createXML(xmlMessage, "register_failure");
            
                        session.sendMessage(doc, out);
                    
                    }
            
                }    
    
            }
     
  4. Valdijiu

    Valdijiu Power Member

    Boas, ja fiz um IM, em .NET(wcf) e em Java(sockets), penso que o raciocínio seja o mesmo..:007:

    Tas a utilizar XML para os Clientes "falarem" com o Servidor? Interessante.

    Mas não percebi a maneira como estás a guardar os teus utilizadores. É numa variável no servidor certo?

    Sabes o que são "Race Conditions" ?
     
  5. xuxaki

    xuxaki Power Member

    Como assim?!? O que fica no Vector? Só o primeiro user logado? O ultimo?
    Eu já fiz algo do género, e o vector (no meu caso era uma Hashtable, pois é muito rápido e já é synchronized por defeito...) de utilizadores deve estar sempre actualizado no servidor principal, e não nas Threads que crias para cada cliente.

    Os métodos que devem ser synchronized são os que podem ser acedidos por mais que uma Thread ao mesmo tempo... No teu caso parece-me que o método que faz o parsing deve ser o unico synchronized, pois os outros métodos são chamados a partir dele! Desta forma estás a precaver problemas com esses métodos.
     
  6. lilcrazy

    lilcrazy Power Member

    Sim é bastante interessante :) Eu estou a guardar os utilizadores (Objectos User) num vector (Vector<User> users).

    Estou a carregar esse vector, lendo do ficheiro se existir, no servidor.

    Depois por cada thread, no método run, (que tinha a synchronized e alterei, agora já funciona melhor)

    Código:
        //=============================
        public void run() {
    
            try {
    
                while(true) {
                    
                    // re-loads all data in each iteration
                    users = db.getRegisteredUsers();
    
                   (etc...)
               }
           }
       }
    Na parte (etc...) eu estou a fazer o parsing, e dependendo do resultado, chamo os métodos "autentica()", "mudaEstado()" e por ai em diante.

    É mais correcto colocar o método run a synchronized como tinha antes e tirar o synchronized de cada outro método que tenha? (autentica, etc...). Ou fazer um novo método synchronized parsing() dentro do método run e dentro desse método chamo os métodos normais (autentica, etc...)?

    Tenho um vector users, como já disse, lidos ao ínicio no servidor. Para manusear esse vector estou a fazê-lo, como viram em cada thread. O ideal seria utilizar a variável users (Vector) do servidor, (existente em XMPPServer.java) pelas threads? Ou seja, a ler na thread tenho

    Código:
     users = db.getRegisteredUsers();
    O melhor é substituir por

    Código:
    XMPPServer.users = db.getRegisteredUsers();
    Sendo que db pertence à classe DataBase que tem os métodos de escrita e leitura de vectores em ficheiros.

    Obrigado pela rápida ajuda até agora fornecida.
     
  7. Valdijiu

    Valdijiu Power Member

    "É mais correcto colocar o método run a synchronized como tinha antes e tirar o synchronized de cada outro método que tenha? (autentica, etc...). Ou fazer um novo método synchronized parsing() dentro do método run e dentro desse método chamo os métodos normais (autentica, etc...)?"

    Utilizas o "synchronized" para delimitar código que aceda a variáveis partilhas. percebes?


    Tipo, tens a variável dos utilizadores no teu servidor: "(Vector<User> users)."
    Porque não a declaras do tipo global e acedes a partir dos teu cliente? é assim que tens feito?
     
  8. xuxaki

    xuxaki Power Member

    Exacto. É assim que deves fazer.
    E a pouco fiz confusão, o parsing é feito nas Threads dos utilizadores, e bem. Depois é que chamas os métodos na class principal do servidor, do tipo:

    Class ServidorPrincipal
    Código:
    Vector users; // global
    
    public synchronized qqcoisa regista(...){
      Inserir user no vector
    }
    
    public synchronized qqcoisa2 autentica(...){
      Operação com vector users
    }
    
    public synchronized User getUserData(String user){
      return users.get(user); // No teu caso que é vector não é este metodo
    }
    
    etc...
    
    Class ThreadClient
    Código:
    ServidorPrincipal referenciaParaServidorPrincipal;
    public run(){
      [I]Recebe XMl e faz o parse
    
    [/I]  [I]conforme o pedido chama os metodos do servidor principal[/I]
    
      referenciaParaServidorPrincipal.regista(...);
    
      [I]Se por acaso precisares de dados do vector users, não tens de estar sempre a fazer "refresh"[/I]
      referenciaParaServidorPrincipal.getUserData(user);
    }
    
    Assim, sempre que precisas de qualquer coisa que está na class principal do servidor, chamas métodos dessa mesma class, na qual os dados estão sempre actualizados! Não faz sentido estares a copiar os dados para as Threads, para alem de te poder dar problemas de coerencia também cria overhead...

    Isto é apenas uma sugestão, não estou a dizer que esta "é a forma correcta".
    Espero ter ajudado
     

Partilhar esta Página