[RESOLVIDO] Pool de threads

Olá Pessoal,

estou desenvolvendo uma aplicação onde uma classe lê dados de joysticks e armazena estes dados (botões pressionados etc.) em um buffer, que é lido posteriormente por outra classe que processa os tais dados.

Estou usando um newCachedThreadPool para lanças essas classes em threads (estou usando Runnable) e tudo vai bem o problema é que, mesmo depois do método run() terminar, a thread não vai para dead, ficando em sleep ou waiting eternamente. Com isso, quando lanço outras threads, ao invés de usar os “slots” das que já terminaram, o newCachedThreadPool cria novos “slots” consumindo mais recursos.

Em tempo, estou usando um while(!encerrar) {… } para controlar a execução do método run(), no final da execução faço encerrar = true.

Hehehehe faltou a pergunta :slight_smile:

Alguém sabe como resolver isso? :stuck_out_tongue:

As threads depois de voce fazer o encerrar = true.
voce tem que lançar um stop para a thread realmente acabar.

Mas segundo a API o método stop() está deprecated, veja:

http://java.sun.com/javase/6/docs/api/java/lang/Thread.html#stop()

Não testei com esse cara, mas acredito que funcionaria, mas essa seria a única maneira?

Não é para parar a thread na marra.

Segundo a documentação, a thread deveria ficar ativa por até 60 segundos se não estiver ocupada.

Se precisar mudar esses parâmetros, em vez de usar Executors.newCachedThreadPool, use um dos construtores de ThreadPoolExecutor, que permite um “fine tuning” melhor.

Nunca usei tanto Threads por isos não sabia ams acho melhor usar a ideia do thingol.

[quote=thingol]Não é para parar a thread na marra.

Segundo a documentação, a thread deveria ficar ativa por até 60 segundos se não estiver ocupada.

Se precisar mudar esses parâmetros, em vez de usar Executors.newCachedThreadPool, use um dos construtores de ThreadPoolExecutor, que permite um “fine tuning” melhor. [/quote]

Eu vi essa informação na API, rodei o programa, fiz o atributo encerrar = true e esperei os 60 segundos e nada. As threads ficam em sleeping e não liberam os slots do newCachedThreadPool. Tentei achar um meio de setar o status delas como Dead, mas não achei nada.

Mesmo usando o shutdownnow() não resolveu, pq o shutdown não é garantido.

Bom, resolvido.

A questão é que as threads não passam para o status DEAD, elas ficam com o status WAINTING até sairem do pool, depois dos tais 60 segundos.

Para complementar, fiz uns ajustes baseado nesse exemplo:

http://www.java2s.com/Code/Java/Threads/Anotherwaytostopathread.htm

O código ficou assim:

package modelo;

/**
 *
 * @author Renato Pinheiro
 */
public class Produtor implements Runnable {
    
    private int numero = 0;
    private Buffer buffer;
    
    private volatile boolean stopRequested;
    private Thread runThread;    

    public Produtor(Buffer buffer) {
        this.buffer = buffer;
    }

    @SuppressWarnings("empty-statement")
    public void run() {
        
        runThread = Thread.currentThread();
        stopRequested = false;        
        
        while(!stopRequested){
            
            buffer.put(numero++);
            
            try {
                Thread.sleep(20);
            } catch (InterruptedException ex) {
                // re-assert interrupt
                Thread.currentThread().interrupt();
            }
            
        }
                
    }
    
    public void stopRequest() {
        stopRequested = true;

        if (runThread != null) {
          runThread.interrupt();
        }
        
    }    
        
}
package modelo;

/**
 *
 * @author Renato Pinheiro
 */
public class Consumidor implements Runnable {
    
    private Buffer buffer;

    private volatile boolean stopRequested;
    private Thread runThread;      

    public Consumidor(Buffer buffer) {
        this.buffer = buffer;
    }

    @SuppressWarnings("empty-statement")
    public void run() {
        
        runThread = Thread.currentThread();
        stopRequested = false;        

        while(!stopRequested){
            
            int numero = buffer.take();
            
            System.out.println(numero);
            
            try {
                Thread.sleep(20);
            } catch (InterruptedException ex) {
                // re-assert interrupt
                Thread.currentThread().interrupt();
            }
            
        }

    }
    
    public void stopRequest() {
        stopRequested = true;

        if (runThread != null) {
          runThread.interrupt();
        }
        
    }     
    
}
package modelo;

import java.util.concurrent.ArrayBlockingQueue;

/**
 *
 * @author Renato Pinheiro
 */
public class Buffer {
    
    private ArrayBlockingQueue<Integer> buffer = 
            new ArrayBlockingQueue<Integer>(30);
    
    @SuppressWarnings("empty-statement")
    public int take(){
        
        int numero = 0;
        
        try {

            numero = buffer.take();

        } catch (InterruptedException ex) {
            ;
        }
        
        return numero;
        
    }
    
    @SuppressWarnings("empty-statement")
    public void put(int numero){
        try {
            buffer.put(numero);
        } catch (InterruptedException ex) {
            ;
        }
        
    }

}
/*
 * Principal.java
 *
 * Created on 2 de Junho de 2008, 23:45
 */

package aplicacao;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import modelo.Buffer;
import modelo.Consumidor;
import modelo.Produtor;

/**
 *
 * @author  Renato
 */
public class Principal extends javax.swing.JDialog {
    
    private boolean existe = false;
    private Produtor produtor;
    private Consumidor consumidor;
    private Buffer buffer;
    private ExecutorService executor = Executors.newCachedThreadPool();
        
    /** Creates new form Principal */
    public Principal(java.awt.Frame parent, boolean modal) {
        super(parent, modal);
        initComponents();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {

        jbtnIniciar = new javax.swing.JButton();
        jbtnParar = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);

        jbtnIniciar.setText("Iniciar / Reiniciar");
        jbtnIniciar.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jbtnIniciarActionPerformed(evt);
            }
        });

        jbtnParar.setText("Parar");
        jbtnParar.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jbtnPararActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(jbtnParar, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 186, Short.MAX_VALUE)
                    .addComponent(jbtnIniciar, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 186, Short.MAX_VALUE))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jbtnIniciar)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(jbtnParar)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>

    @SuppressWarnings("empty-statement")
private void jbtnIniciarActionPerformed(java.awt.event.ActionEvent evt) {                                         

    if(!existe){
        
        buffer = new Buffer();
        consumidor = new Consumidor(buffer);        
        produtor = new Produtor(buffer);

        executor.execute(produtor);
        executor.execute(consumidor);  
        existe = true;                
    }
    
}

private void jbtnPararActionPerformed(java.awt.event.ActionEvent evt) {                                         
        
    if(this.existe){
        produtor.stopRequest();
        consumidor.stopRequest();
        buffer = null;
        produtor = null;
        consumidor = null;
        this.existe = false;
    }

}

    /**
    * @param args the command line arguments
    */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                Principal dialog = new Principal(new javax.swing.JFrame(), true);
                dialog.addWindowListener(new java.awt.event.WindowAdapter() {
                    public void windowClosing(java.awt.event.WindowEvent e) {
                        System.exit(0);
                    }
                });
                dialog.setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify
    private javax.swing.JButton jbtnIniciar;
    private javax.swing.JButton jbtnParar;
    // End of variables declaration


}

Aliás, manter as threads vivas é o que torna o thread pool eficiente.

A idéia geral é reaproveitar uma linha de execução, evitando os seus tempos de criação e destruição, quando threads de curta duração são criadas.

Se vc matar cada linha de execução logo em seguida que o método run termina, sempre terá esse overhead, que as vezes pode deixar a aplicação indesejavelmente lenta.

Por isso os tais 60 segundos. :wink: