Olá pessoal,
Estou estudando para a certificação Java e estou na parte sobre Threads mas estou com dificuldades em entender a parte sobre wait, notify e notifyall. Tenho duas dúvidas.
1 dúvida é referente a esse código:
package waitNotify;
public class Leitor extends Thread {
Calculator c;
public Leitor(Calculator calc){
c = calc;
}
public void run(){
synchronized(c){
try{
System.out.println("Aguardando a finalização do cálculo...");
c.wait();
} catch(InterruptedException e){ }
}
System.out.println("O total é: " + c.total);
}
public static void main(String[] args) {
Calculator calculo = new Calculator();
new Leitor(calculo).start();
new Leitor(calculo).start();
new Leitor(calculo).start();
calculo.start();
}
}
package waitNotify;
public class Calculator extends Thread {
int total;
public void run(){
synchronized(this){
for(int i = 0; i<100; i++){
total += 1;
notifyAll();
}
}
}
}
A saída é:
Aguardando a finalização do cálculo…
Aguardando a finalização do cálculo…
Aguardando a finalização do cálculo…
O total é: 100
O total é: 100
O total é: 100
Vamos a minha dúvida… no método main é criado um objeto Calculator(que é uma thread) e esse objeto é passado como argumento para 3 instâncias de Leitor(que também são threads). Pelo que eu havia lido, quando chamo uma thread e passo um objeto no argumento seria chamado o método run desse objeto passado como argumento(ele é o destino, se fosse um Runnable poderia ser chamado de Runnable de destino), correto?
Então quando eu crio as 3 instâncias de Leitor não deveria chamar 3 vezes o método run que faz o cálculo do total? Pq então parece que chamou 3 vezes o método run de Leitor(aparecem 3 mensagens ‘Aguardando a finalização do cálculo…’) ? Porque entra primeiro no método run de Leitor ao invés de entrar no run de Calculator já que o objeto Calculator foi passado 3 vezes como argumento?
Explicando melhor minha dúvida, pelo que eu entendi seria isso:
new Leitor(calculo).start(); //deveria chamar run de Calculator
new Leitor(calculo).start(); //deveria chamar run de Calculator
new Leitor(calculo).start(); //deveria chamar run de Calculator
calculo.start(); //deveria chamar run de Calculator
Não entendo como e porque o método run de Leitor foi chamado.
A dúvida 2 tem a ver com outro código:
package waitNotify;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class Controlador extends JFrame {
private JButton btnPausa = null;
private JScrollPane scrlTexto = new JScrollPane();
private JTextArea txtArea = new JTextArea();
private Impressora impressora;
public Controlador() {
super("Demonstração do wait e notify");
setLayout(new BorderLayout());
add(getBtnPausa(), BorderLayout.NORTH);
txtArea.setEditable(false);
scrlTexto.add(txtArea);
scrlTexto.setViewportView(txtArea);
add(scrlTexto, BorderLayout.CENTER);
setSize(640,480);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
impressora = new Impressora(txtArea);
}
private JButton getBtnPausa() {
if (btnPausa == null) {
btnPausa = new JButton("Pausa");
btnPausa.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if (btnPausa.getText().equals("Pausa"))
{
btnPausa.setText("Continua");
impressora.setPausado(true);
return;
}
btnPausa.setText("Pausa");
impressora.setPausado(false);
}
});
}
return btnPausa;
}
public static void main(String args[]) {
new Controlador().setVisible(true);
}
}
package waitNotify;
import javax.swing.JTextArea;
public class Impressora {
private JTextArea txtDestino = null;
private long linha = 0;
private boolean pausado = false;
public Impressora(JTextArea txtDestino) {
if (txtDestino == null)
throw new NullPointerException("Destino não pode ser nulo!");
this.txtDestino = txtDestino;
//Disparamos a thread da impressora.
Thread t = new Thread(new ImpressoraRun(), "Thread da impressora");
t.setDaemon(true);
t.start();
}
private synchronized void verificaPausa() throws InterruptedException {
while (pausado) {
wait();
}
}
public synchronized void setPausado(boolean pausado) {
this.pausado = pausado;
if (!this.pausado)
notifyAll();
}
private void imprime()
{
StringBuilder msg = new StringBuilder("Linha ");
msg.append(Long.toString(linha++ % Long.MAX_VALUE));
msg.append("\n");
txtDestino.append(msg.toString());
}
private class ImpressoraRun implements Runnable {
public void run() {
try {
while (true) {
verificaPausa();
imprime();
Thread.sleep(500);
}
} catch (InterruptedException e) {
txtDestino.append("Processamento da impressora interrompido.");
}
}
}
}
Dúvida:
Aqui é disparada a Thread
Thread t = new Thread(new ImpressoraRun(), “Thread da impressora”);
t.setDaemon(true);
t.start();
Então vai rodar o run da classe ImpressoraRun que vai chamar o método verificaPausa onde tem um wait(). Se eu clicar no botão da interface gráfica é chamado o método setPausado onde tem um notifyAll().
Minha dúvida é a seguinte, se tenho duas threads rodando(a do main que é a principal do programa e a que eu disparei que é a da ImpressoraRun) quando é chamado wait e notifyAll como o Java sabe que tem que pausar ou notificar a thread ImpressoraRun e não a thread principal. E se eu tivesse 10 threads por exemplo como o Java saberia qual pausar e qual notificar?
Desculpem o post grande, mas essas 2 dúvidas estão me matando. E não sei se ficaram claras as dúvidas mas qualquer coisa eu explico melhor se alguém não entender o que escrevi.
Obrigado pessoal, abraço.