Olá Pessoal…
Tenho um método que pinta linhas de uma JTable através de um CellRenderer… até aí tudo blz, o problema é que antes de pintá-la eu preencho-a com dados do DB. Esta pesquisa no banco é feita em uma Thread separada da main, mas se eu coloco o método de pintura junto nesta Thread NPEs exporádicos acontecem, devido à este método ser chamado por outra Thread (não diretamente a main). Como posso resolver esse problema?
Obs.:
1 - As linhas são pintadas mesmo estourando os NPEs…
2 - Preciso colocar a pintura da tabela na Thread main para as linhas serem pintadas…
3 - Já fizemos uma thread esperar a outra e mesmo assim não adiantou (justamente pelo fato da pintura estar em uma Thread separada da main)
Pergunta boba número 1. Você precisa mostrar nessa JTable coisas que não são strings, é isso?
erico_kl
Sim e Não
Na verdade mostro Strings, Booleans (como CheckBox), Doubles…
E
entanglement
Mas não é que precisa mostrar cores, ícones ou até gráficos (como eu já vi acontecendo). É só aquelas coisas costumeiras, não?
E
entanglement
No seu caso, em que você precisa mostrar rapidamente atualizações em uma JTable, eu aconselharia você usar o GlazedLists.
Eu passei a usar o GlazedLists para mostrar atualizações de cotações da Bolsa em tempo real (sendo que o meu programa tem uma tonelada de threads diferentes), e não tive problemas com esse tipo de coisa que você está reportando.
O problema em si não está em carregar os dados rapidamente ou em pintar as linhas (é só pintar as linhas mesmo, não tem gráficos não…), mas sim em fazer essa pintura atualizar a table de dentro de uma Thread… ou fazer com que a main atualize a table depois da execução da outra Thread…
E
entanglement
E é por isso que saber usar corretamente o GlazedLists é uma boa. Basicamente as coisas funcionam assim: você cria uma classe que representa 1 linha de sua JTable. Digamos que essa classe se chame “LinhaTabela”. Então crie uma lista desses objetos (que pode ser ordenada, se quiser) chamada BasicEventList, e então para:
a) Inserir / remover / modificar dados nessa tabela - pegue um WriteLock que está associado a essa lista, chame o método lock(), faça a operação desejada, e então chame o método unlock().
b) Simplesmente ler alguma coisa dessa tabela - pegue um ReadLock que está associado a essa lista, chame o método lock(), faça a operação desejada, e então chame o método unlock().
O conceito é que 1 “writer” pode estar ativo em um determinado tempo, mas N “readers” podem estar ativos nesse mesmo tempo.
Os métodos de inserção e modificação na lista disparam aqueles métodos “updateXXX” que servem para redesenhar a tabela, mas são disparados na thread do Swing, não na sua própria thread, o que torna isso mais seguro.
erico_kl
hmm… certo
vou estudar aqui o GlazedLists e tentar implementar algo em cima desse problema…
Detalhe: dessa maneira eu não conseguiria mais trabalhar com TableModels? (Corrija-me se eu estiver errado)
E
entanglement
Ahá! O GlazedLists implementa uma TableModel para você. De qualquer maneira, pegue este programa e compile com a versão mais nova do GlazedLists. Você vai ver (clicando o botão “Adicionar Multithread”) que duas threads estão atualizando a tabela simultaneamente, sem dar problemas. Você pode até continuar a usar os outros botões.
packageguj;importjava.awt.BorderLayout;importjava.awt.FlowLayout;importjava.util.Random;importjavax.swing.JButton;importjavax.swing.JFrame;importjavax.swing.JPanel;importjavax.swing.JScrollPane;importjavax.swing.JTable;importjavax.swing.ListSelectionModel;importjavax.swing.SwingUtilities;importca.odell.glazedlists.BasicEventList;importca.odell.glazedlists.EventList;importca.odell.glazedlists.swing.EventTableModel;importca.odell.glazedlists.util.concurrent.Lock;importjava.awt.Dimension;/** * Este exemplo requer o uso da seguinte biblioteca: Glazed Lists - http://publicobject.com/glazedlists/ */publicclassExemploGlazedListsMultithreadextendsJFrame{publicstaticclassClienteimplementsComparable<Cliente>{publicCliente(finalStringnome,finalStringtelefone,finalintcodigo){this.nome=nome;this.telefone=telefone;this.codigo=codigo;}@OverridepublicStringtoString(){returngetNome();}publicStringgetNome(){returnnome;}publicStringgetTelefone(){returntelefone;}publicintgetCodigo(){returncodigo;}@OverridepublicintcompareTo(Clientethat){returnInteger.valueOf(this.codigo).compareTo(that.codigo);}privateStringnome;privateStringtelefone;privateintcodigo;}privatestaticfinallongserialVersionUID=1L;privateJPaneljContentPane=null;privateJScrollPanescpClientes=null;privateJButtonbtnAdicionar=null;privateJButtonbtnRemover=null;privateJButtonbtnLimpar=null;privateJPanelpnlBotoes=null;privateEventList<Cliente>clientes=newBasicEventList<Cliente>();privateEventTableModel<Cliente>modelClientes;privateintcodigo;privateRandomrand=newRandom();privateJTabletblClientes=null;privateJButtonbtnAdicionarMultithread=null;privateJScrollPanegetScpClientes(){if(scpClientes==null){scpClientes=newJScrollPane();scpClientes.setViewportView(getTblClientes());}returnscpClientes;}privateJButtongetBtnAdicionar(){if(btnAdicionar==null){btnAdicionar=newJButton();btnAdicionar.setText("Adicionar");btnAdicionar.addActionListener(newjava.awt.event.ActionListener(){publicvoidactionPerformed(java.awt.event.ActionEvente){Locklock=clientes.getReadWriteLock().writeLock();try{lock.lock();clientes.add(newCliente("Cliente "+codigo,"888-8889",codigo));codigo++;}finally{lock.unlock();}}});}returnbtnAdicionar;}privateJButtongetBtnRemover(){if(btnRemover==null){btnRemover=newJButton();btnRemover.setText("Remover");btnRemover.addActionListener(newjava.awt.event.ActionListener(){publicvoidactionPerformed(java.awt.event.ActionEvente){Locklock=clientes.getReadWriteLock().writeLock();try{lock.lock();// Removendo os clientes selecionadosListSelectionModellsm=tblClientes.getSelectionModel();while(!lsm.isSelectionEmpty()){clientes.remove(lsm.getMinSelectionIndex());}}finally{lock.unlock();}}});}returnbtnRemover;}privateJButtongetBtnLimpar(){if(btnLimpar==null){btnLimpar=newJButton();btnLimpar.setText("Limpar");btnLimpar.addActionListener(newjava.awt.event.ActionListener(){publicvoidactionPerformed(java.awt.event.ActionEvente){Locklock=clientes.getReadWriteLock().writeLock();try{lock.lock();clientes.clear();}finally{lock.unlock();}}});}returnbtnLimpar;}privateJPanelgetPnlBotoes(){if(pnlBotoes==null){pnlBotoes=newJPanel();pnlBotoes.setLayout(newFlowLayout());pnlBotoes.add(getBtnAdicionar());pnlBotoes.add(getBtnRemover());pnlBotoes.add(getBtnLimpar());pnlBotoes.add(getBtnAdicionarMultithread());}returnpnlBotoes;}privateJTablegetTblClientes(){if(tblClientes==null){tblClientes=newJTable();modelClientes=newEventTableModel<Cliente>(clientes,newString[]{"nome","telefone","codigo"},newString[]{"Nome","Telefone","Código"},newboolean[]{false,false,false});tblClientes.setModel(modelClientes);}returntblClientes;}privatevolatilebooleanflagTerminou;privateJButtongetBtnAdicionarMultithread(){if(btnAdicionarMultithread==null){btnAdicionarMultithread=newJButton();btnAdicionarMultithread.setText("Adicionar Multithread");btnAdicionarMultithread.addActionListener(newjava.awt.event.ActionListener(){publicvoidactionPerformed(java.awt.event.ActionEvente){btnAdicionarMultithread.setEnabled(false);Threadthr1=newThread(newRunnable(){@Overridepublicvoidrun(){for(inti=1;i<=1000;++i){Locklock=clientes.getReadWriteLock().writeLock();try{lock.lock();clientes.add(newCliente("Thread 1 "+codigo,"7777-7777",codigo));codigo++;}finally{lock.unlock();}try{Thread.sleep(20);}catch(InterruptedExceptionex){}}if(flagTerminou)btnAdicionarMultithread.setEnabled(true);flagTerminou=true;}});Threadthr2=newThread(newRunnable(){@Overridepublicvoidrun(){for(inti=1;i<=1000;++i){Locklock=clientes.getReadWriteLock().writeLock();try{lock.lock();clientes.add(newCliente("Thread 2 "+codigo,"5555-5555",codigo));codigo++;}finally{lock.unlock();}try{Thread.sleep(10);}catch(InterruptedExceptionex){}}if(flagTerminou)btnAdicionarMultithread.setEnabled(true);flagTerminou=true;}});thr1.start();thr2.start();}});}returnbtnAdicionarMultithread;}publicstaticvoidmain(String[]args){SwingUtilities.invokeLater(newRunnable(){publicvoidrun(){ExemploGlazedListsMultithreadthisClass=newExemploGlazedListsMultithread();thisClass.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);thisClass.setVisible(true);}});}publicExemploGlazedListsMultithread(){super();initialize();}privatevoidinitialize(){this.setSize(525,257);this.setContentPane(getJContentPane());this.setTitle("Exemplo Glazed Lists - JList");}privateJPanelgetJContentPane(){if(jContentPane==null){jContentPane=newJPanel();jContentPane.setLayout(newBorderLayout());jContentPane.add(getScpClientes(),BorderLayout.CENTER);jContentPane.add(getPnlBotoes(),BorderLayout.SOUTH);}returnjContentPane;}}// @jve:decl-index=0:visual-constraint="10,10"
erico_kl
Bah… valeu pela ajuda, vou estudar com mais calma à noite e aí retorno aqui qualquer coisa…
Implementando dessa maneira eu continuo colorindo a tabela com TableCellRenderer, certo?
erico_kl
peguei o seu exemplo mas ele gera vários erros…
inclusive o próprio exemplo em web start do site está rodando com erros na console…
o meu problema é somente os estouros de NPE, pois tudo funciona perfeitamente… acho que vou fazer simplesmente um:
try{...}catch(NullPointerExceptionnpe){}
pois mesmo estourando o erro a tabela é carregada, pintada e atualizada na Thread sem problemas…
esse é o erro listado:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at javax.swing.plaf.synth.SynthTableUI.paintCell(Unknown Source)
at javax.swing.plaf.synth.SynthTableUI.paintCells(Unknown Source)
at javax.swing.plaf.synth.SynthTableUI.paint(Unknown Source)
at javax.swing.plaf.synth.SynthTableUI.update(Unknown Source)
at javax.swing.JComponent.paintComponent(Unknown Source)
at javax.swing.JComponent.paint(Unknown Source)
at javax.swing.JComponent.paintToOffscreen(Unknown Source)
at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(Unknown Source)
at javax.swing.RepaintManager$PaintManager.paint(Unknown Source)
at javax.swing.RepaintManager.paint(Unknown Source)
at javax.swing.JComponent._paintImmediately(Unknown Source)
at javax.swing.JComponent.paintImmediately(Unknown Source)
at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
at javax.swing.RepaintManager.seqPaintDirtyRegions(Unknown Source)
at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(Unknown Source)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
erico_kl
Olá pessoal…
Resolvi o problema adicionando um ComponentListener para componentResized que cada vez que muda o tamanho da tabela (toda a pesquisa vai mudar pois o tableModel é zerado antes de cada pesquisa), as linhas são pintadas…
Assim a pintura roda na Thread main e sempre depois de cada filtragem.
Resultado: não deu mais erros e não precisei fazer gambiarras do tipo “catch (Exception) {}” somente para não dar o stack trace do erro…