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
}