Pessoal, to com uma dúvida meio boba, mas que não achei resposta completa (leia-se que tenha me feito aceitar como verdadeira).
A situação é a seguinte: tenho uma aplicação que possui um ServerSocket. Cada vez que ele recebe alguma requisição ele cria uma nova instância de um socket para atendê-la e volta a ouvir a porta, de forma que posso ter varias threads atendendo varias requisições simultaneamente. Acredito que até ai está correto né, de forma a não impedir que, enquanto atenda uma requisição, meu servidor fique travado sem receber outras certo?
O problema é que esse servidor, alem do serverSocket, tem um outro parametro que é um controlador do meu sistema, e para cada thread de serviço que ele cria, ele passa esse controlador como parametro. A thread de serviço precisa notificar o controlador de alguns eventos da requisição.
Como podem haver várias threads de serviço, o mesmo controlador poderia estar sendo acionado diversas vezes por threads diferentes.
Se elas estiverem acessando métodos diferentes acredito não haver problema correto?
Mas e se estiverem acessando o mesmo método, poderia ocorrer erro? Imaginei que sim, então assinei o método que é acessado pelas threads de serviço como synchronized, isso ja resolve a questão?
Até aqui tudo bem, embora essa solução não permita um número muito grande de conexões simultaneas. Se seu sistema for atender a uma demanda realmente significativa, considere a possibilidade de usar a classe SocketChannel e os Selectors. Assim, vc também poderá reduzir significativamente o número de threads.
Desde que esses métodos não acessem nenhum atributo da sua classe em comum, sim, isso é correto. Acessar um único atributo sequer significará compartilhar memória entre 2 ou mais threads, o que é a maior causa de problemas nesse tipo de sistema.
Aliás, por motivos de performance, cada thread também pode criar um cache local de um atributo. Ou seja, duas threads podem enxergar valores diferentes sobre um mesmo objeto. Esse cache não é usado em duas situações:
Métodos synchronized: Já que para garantir sincronização é necessário usar valores atuais de variáveis, não cacheados;
Atributos com o modificador volatile: Atributos declarados como volatile não usam esse cache.
Mesmo caso da situação anterior. Se o método em questão só tiver variáveis locais, cada thread criará sua própria instância, no seu stack, e tudo irá rodar bem. Se acessarem um único atributo, entretanto, a coisa muda de figura.
paulohrl:
Imaginei que sim, então assinei o método que é acessado pelas threads de serviço como synchronized, isso ja resolve a questão?
Isso irá serializar o acesso ao seu objeto controlador, fazendo com que só uma thread possa percorrer o método por vez. Muitas vezes, só isso é suficiente para garantir thread-safety. Por outro lado, se o controlador demorar, pode ser que ele trave o seu sistema inteiro, já que todas as threads terão que esperar para disparar eventos.
Isso pode ocorrer, por exemplo, se um dos listeners do seu controlador realizar uma tarefa demorada, assim que recebe o evento.
Uma das alternativas a essa solução seria criar um controlador diferente para cada thread, se possível. Olhe para isso a classe ThreadLocal. Assim, você mantém o acesso concorrente entre os controladores. Threads diferentes em objetos diferentes não apresentam problemas de sincronização.
Outra alternativa é criar uma thread específica para disparar os eventos do controlador e uma fila de mensagens. Assim, cada thread que disparar um evento no controlador nunca precisará esperar que seus listeners tratem esse evento. O controlador retorna imediatamente, tão logo tenha colocado a requisição do evento na fila. A sua thread worker mais tarde desempilha essa requisição e dispara o evento em si.
Tenha sempre em mente que o maior problema da programação entre threads é lidar com variáveis compartilhadas. Ou seja, duas threads acessando concorrentemente a mesma região de memória.
P
paulohrl
Valew ViniGodoy.
A situação é que o meu controlador vai receber da thread de serviço, em alguns casos, uma String, e escrever essa String em um JTextArea da Interface Gráfica apenas. Como é um processo rápido, acredito que não haveria problema certo, e apenas o synchronized no método que será chamado já é o suficiente né?
Outra situação em que ele será chamado seria no caso de finalizar o SocketServer, ja que a classe servidor, que possui o atributo ServerSocket, é um parametro do Controlador. Mas nesse caso, não iria ocorrer de mais de uma thread chamar esse método, então não há problema com ele.
Em relação ao SocketChannel e Selectors, vou dar uma olhada, mas acredito que, para essa primeira versão da aplicação, que deverá atender não mais do que 50, no máximo 100, requisições por minuto, a solução que utilizei atenda, não?
Obrigado
ViniGodoy
Atende e sobra.
No caso, o método append do JTextArea já é sincronizado. Se seu método só escreve a string, não haverá necessidade de nenhum mecanismo adicional de sua parte.
Acho que vc pode ir tranquilo. Pelo seu post vc está entendendo direito o conceito de thread e aparentemente sabe em que campo tenebroso está pisando.
Não vou dizer que você não vá ter dores de cabeça terríveis e chatas, mas é parte do processo. Dificilmente se programa multithread sem várias delas e aproveite a experiência que cada uma irá trazer. Ces’t la vie.
P
paulohrl
Brigadão ViniGodoy, acho que entendi como tratar isso.
É, realmente eu já apanhei bastante dessas benditas threads, e to esperando apanhar ainda bastante rs.
Desculpa aproveitar o post, mas notei, em outros posts, que vc meche, ou mecheu, bastante com swing, então acho que você talvez possa me ajudar com uma outra coisa. Tenho uma progress bar, e uma classe task, que implementa swing worker, para ir atualizando a progress bar. A classe na qual tenho a progress bar extende JPanel e implementa PropertyChangedListener. Minha classe task tem um atributo, teoricamente, quando ele fosse alterado deveria ser chamado o método de alteração de propriedade na classe q extende JPanel não? Testei de diversos jeitos e não funcionou, mas quando eu, após alterar o valor da propriedade, usei o método firePropertyChange, funcionou que é uma beleza. É normal isso ou fiz alguma besteira que o listener não reconhece a alteração do atributo?