Problemas com thread - fila de execução utilizando vector

2 respostas
Dieval_Guizelini

Senhores,

estou com um problema no código abaixo:

public class Executores extends Thread {
    public final static int TREINAMENTO = 1;
    public final static int TESTE = 2;
    private boolean executando = true;
    private static java.util.List<ExecutorParametro> acoes = new java.util.Vector<ExecutorParametro>();

    public Executores() {
    }

    public static synchronized int getQuantidadeAcoesPendentes() {
        return acoes.size();
    }

    public synchronized ExecutorParametro getAcao() {
        if (acoes.size() > 0) {
            return acoes.remove(0);
        }
        return null;
    }

    @Override
    public void run() {
        while (isExecutando()) {
            ExecutorParametro exec = getAcao();
            if( exec != null) {
                if (exec.getTipo() == TREINAMENTO) {
                    exec.executaTreinamento();
                } else {
                    exec.executaTeste();
                }
            } else {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ex) {
                    Logger.getLogger(Executores.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            Thread.yield();
        }
    }

    public static synchronized void addAcao(int tipoAcao, Rede rede, Classe classe,
            double[] carac, SubGerenteTreinamento treina) {
        ExecutorParametro pe = new ExecutorParametro(tipoAcao, rede, classe, carac, treina);
        acoes.add(pe);
    }

    public synchronized boolean isExecutando() {
        return executando;
    }

    public synchronized void setExecutando(boolean v) {
        executando = v;
    }
}

Gerando a exception:

Exception in thread "Thread-9" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 0 at java.util.Vector.remove(Vector.java:777) at br.ufpr.sibila.Executores.getAcao(Executores.java:28)

Alguém tem alguma sugestão?

vw

2 Respostas

J

Bem, vamos por partes:

0. Eu não usuaria o batido padrão "final static int". Basta usar um enum por questões de segurança e confiabilidade:

public enum Tipo {
         TREINAMENTO, TESTE
}

1. Em primeiro lugar vc deve colocar a variável "executando" como volatile, pois assim vc evita problemas com cache nessa variável flag em ambiente de concorrência:

private volatile boolean executando = true;

2. A classe Vector é um tipo legado. É aconselhável que vc utilize:

private static java.util.List<ExecutorParametro> acoes =Collections.synchronizedList(new ArrayList<ExecutorParametro>());

3. Ou então pelo que eu entendi vc tá implementando uma fila concorrente blocante. Então por que não utilizar java.util.concurrent.LinkedBlockingQueue ao invés do malhado Vector? Teu código vai ficar mais suncito, tipo assim:

private static java.util.concurrent.BlockingQueue<ExecutorParametro> acoes = new java.util.concurrent.LinkedBlockingQueue<ExecutorParametro>();  
   
     public Executores() {  
     }  
   
     // não precisa de synchronized, pois acoes já é thread-safe
     public static int getQuantidadeAcoesPendentes() {  
         return acoes.size();
     }  
   
     // vc colocou como public. Tem que ser public????
     private ExecutorParametro getAcao() {  
             return acoes.take(); // remove o 1o. elemento da fila e bloqueia se não existir nenhum.  
     }  
   
   @Override public void run() {  
         while (isExecutando()) {  
             ExecutorParametro exec = getAcao();  
             if( exec != null) {  
                switch (exec.getTipo()) {
                      case TREINAMENTO: exec.executaTreinamento(); break;
                      case TESTE: exec.executaTeste();
                 }
             } 
     }  
   
     public static void addAcao(int tipoAcao, Rede rede, Classe classe, double[] carac, SubGerenteTreinamento treina) {  
         acoes.add(new ExecutorParametro(tipoAcao, rede, classe, carac, treina));  
     }  
   
     public boolean isExecutando() {  
         return executando;  // não precisa do synchronized, pois é volatile
     }  
   
     public void setExecutando(boolean v) {  
         executando = v;  
     }
J

Digo, “sucinto”.

Na verdade, vc poderia usar também java.util.concurrent.ArrayBlockingQueue (que será até mais rápido para algumas operações) ao invés do java.util.concurrent.LinkedBlockingQueue.

Testa e vê se dá certo. Também dá uma olhada no framework Executors do java.util.concurrent. Acho que dá pra simplificar ainda mais! :slight_smile:

Criado 4 de novembro de 2008
Ultima resposta 5 de nov. de 2008
Respostas 2
Participantes 2