Tenho uma aplicação que usa threads e para fazer alguns controles eu uso Vector e analisando a mesma vi que muitas threads entram em wait ou ficam em blocked.
Seria o vector que poderia estar causando isso?
Abaixo segue o um stackTrace quando a thread está em blocked:
ava.net.SocketInputStream.socketRead0 ( native code )
java.net.SocketInputStream.read ( unknown source )
sun.nio.cs.StreamDecoder.readBytes ( unknown source )
sun.nio.cs.StreamDecoder.implRead ( unknown source )
sun.nio.cs.StreamDecoder.read ( unknown source )
java.io.InputStreamReader.read ( unknown source )
java.io.BufferedReader.fill ( unknown source )
java.io.BufferedReader.readLine ( unknown source )
java.io.BufferedReader.readLine ( unknown source )
http.Request.setResultServer ( Request.java:50 )
http.Request.sendByGET ( Request.java:199 )
client.http.ClientSDPRequest.send ( ClientSDPRequest.java:117 )
client.http.ClientSDPRequest.reserveAndCommit ( ClientSDPRequest.java:141 )
billing.request.RequestSDPThread.run ( RequestSDPThread.java:153 )
java.util.concurrent.ThreadPoolExecutor$Worker.runTask ( unknown source )
java.util.concurrent.ThreadPoolExecutor$Worker.run ( unknown source )
java.lang.Thread.run ( unknown source )
Outro stackTrace, esse eu acredito que seja causado pelo log4j eu já aumentei o nível do log, melhorou bastante, mas ainda aparece este blocked:
org.apache.log4j.Category.callAppenders ( Category.java:204 )
org.apache.log4j.Category.forcedLog ( Category.java:391 )
org.apache.log4j.Category.info ( Category.java:666 )
billing.control.BillingThread.run ( BillingThread.java:163 )
java.util.concurrent.ThreadPoolExecutor$Worker.runTask ( unknown source )
java.util.concurrent.ThreadPoolExecutor$Worker.run ( unknown source )
java.lang.Thread.run ( unknown source )
Agora abaixo segue um stackTrace quando a thread está em wait:
java.net.SocketInputStream.socketRead0 ( native code )
java.net.SocketInputStream.read ( unknown source )
sun.nio.cs.StreamDecoder.readBytes ( unknown source )
sun.nio.cs.StreamDecoder.implRead ( unknown source )
sun.nio.cs.StreamDecoder.read ( unknown source )
java.io.InputStreamReader.read ( unknown source )
java.io.BufferedReader.fill ( unknown source )
java.io.BufferedReader.readLine ( unknown source )
java.io.BufferedReader.readLine ( unknown source )
http.Request.setResultServer ( Request.java:50 )
http.Request.sendByGET ( Request.java:199 )
client.http.ClientSDPRequest.send ( ClientSDPRequest.java:117 )
client.http.ClientSDPRequest.reserveAndCommit ( ClientSDPRequest.java:141 )
request.RequestSDPThread.run ( RequestSDPThread.java:153 )
java.util.concurrent.ThreadPoolExecutor$Worker.runTask ( unknown source )
java.util.concurrent.ThreadPoolExecutor$Worker.run ( unknown source )
java.lang.Thread.run ( unknown source )
Sim o java.util.Vector é sincronizado, vc realmente precisa de multi-threads?
Poste as áreas em que as threads acessam concorrentemente, e como você está fazendo para sincroniza-las.
Outra coisa. É normal threads ficarem em wait ao chamar o método read. Sua thread que faz write está com problemas?
Vou tentar colocar algumas partes do código e explicar como funciona esta parte da aplicação que usa o vector, porque o tem bastante código e iria ficar meio confuso se postasse aqui.
A parte aonde eu uso vector é aonde eu controlo o número de threads que podem ser usadas. A aplicação vai no banco retorna 500 registros e ai esses registros viram threads. As threads processam os registros do banco e quando acaba o processo ela retira do vector o registro que ela processou.
Abaixo seguem alguns trechos do código:
Thread que pega os registro do banco e coloca no vector:
[code]
while( true ) {
if(!this.customerDAO.getRemainingCustomersCountByChannel(this.getRuleChannel().getIdChannel())){
break;
}
nowTime = Calendar.getInstance();
if( nowTime.after( endTime ) ) {
break;
} else {
bufferThread.setCapacity( this.getLimit() );
bufferSize = bufferThread.size();
remainingCapacity = bufferThread.remainingCapacity();
if( bufferSize >= this.getLimit() ) {
continue;
}
if(this.isRetry()){
customers = this.customerDAO.getCustomersRetryForBilling( this.getRuleChannel().getIdChannel(), remainingCapacity );
} else if (!this.isRetry()){
customers = this.customerDAO.getCustomersForBilling( this.getRuleChannel().getIdChannel(), remainingCapacity );
}
if( customers == null || customers.isEmpty() ) {
LOGGER.debug( token + "There aren't customers to channel " + this.getRuleChannel().getIdChannel() );
continue;
}
// Setando o vector, porque eh ele quem controla a quantidade de threads que eu posso criar por interacao.
bufferThread.addAll(customers);
newCustomers = customers.size();
LOGGER.info( this.token + "Total customers to be charged: " + newCustomers );
Iterator<CustomerRelationChannel> iterator = customers.iterator();
while(iterator.hasNext()){
try {
customer = bufferThread.next();
requestSDPThread = new RequestSDPThread();
requestSDPThread.setApplicationContext( applicationContext );
requestSDPThread.setToken( this.getToken() );
if( this.isRetry() ) {
requestSDPThread.setRetry( 1 );
} else {
requestSDPThread.setRetry( 0 );
}
requestSDPThread.setCustomer( iterator.next() );
requestSDPThread.setRuleChannel( this.getRuleChannel() );
requestSDPThread.setBuffer( bufferThread );
//Thread que "dispara" as threads de request
ThreadControl.EXECUTOR_SERVICE.execute( requestSDPThread );
} catch(Exception e) {
LOGGER.error( this.getToken(), new Throwable( e ) );
}
}
}
}[/code]
Abaixo segue a thread que processa o canal e retira do buffer o customer:
[code]
public void run(){
try {
this.getBuffer().remove( this.getCustomer() );
} catch(Exception e) {
LOGGER.error( this.getToken(), new Throwable( e ) );
}
}[/code]
Uma outra parte do código que pode estar dando problema, que eu acho é essa:
private String resultServer = null;
private BufferedReader fromServer;
private Socket socket;
private void setResultServer() throws IOException{
if(this.socket != null && this.socket.isConnected() && this.isLoggerResponse() ){
double startTime = System.currentTimeMillis();
for(String temp = null; (temp = this.fromServer.readLine()) != null;){
this.resultServer += temp;
}
} else {
this.resultServer = null;
}
}
Eu sempre tenho que pegar o retorno do servidor. Não vejo outra maneira de fazer isso. Alguém teria alguma ideia de como melhorar este método?
É difícil responder sua pergunta, pq não conheço nem o que sua aplicação faz, nem o seu protocolo, e nem o comportamento do seu servidor.
Geralmente, num modelo síncrono de envio/espera pela resposta o que você tem são:
a) Uma thread isolada, que apenas dê dados vindo da rede, e os empacota num pacote mínimo e codificado. Essa classe encaminha os dados para uma thread decodificadora;
b) Uma thread, que faz a decodificação da mensagem. Essa thread usa algum campo de destino, para saber para onde responder a requisição.
c) Uma série de threads clientes. Ao enviar um pacote, a thread cliente cria um objeto local (através de um ThreadLocal) que irá tratar a requisição. Nesse objeto, bloqueia-se a thread cliente. A thread decodificadora contém um mapa desses objetos, e irá notifica-los assim que mensagem chegar, liberando assim a thread cliente para utilizar a informação vinda da rede.
No caso, eu não baseio meus protolocos em \n, justamente para não depender do método readLine. Tipicamente, o que faço é colocar nos primeiros 4 bytes o tamanho da mensagem, e nos 4 seguintes o tipo. Em seguida, a mensagem em si. Essa organização é útil pq a classe que lê da rede só precisa se incomodar com esses 4 primeiros bytes, enquanto a classe decodificadora com os 4 seguintes. O processo então pode ser dividido em camadas, cada uma desempacotando parte do pacote de dados. Esse modelo é explicado aqui:
http://www.guj.com.br/posts/list/136538.java#735860
Caso criptografia seja necessária, vc pode incluir uma camada entre essas duas.