Thread sincronizada

9 respostas
M

Pessoal

Estou precisando fazer com que dois threads consumidor e produtor trabalhem juntos, de forma que o produtor não gere muitos dados e o consumidor não consuma todos os dados. alguém tem alguma ideia de como posso fazer ?

Vou detalhar agora, estou lendo um arquivo texto (grande) linha a linha ( e este é o trabalho do produtor) e faço um parser e gravou em outro(s) arquivos (e este é o trabalho do consumidor).

Se o produtor trabalha muito mais rápido que o consumidor, fico com muitas linhas na memória e fico sem memória.

Se o consumidor trabalha muito rápido, fico sem buffer e o consumidor fica roubando tempo de processamento do produtor para não fazer nada.

O que gostária de fazer serial algo mais ou menos assim:

Se buffer > 1000 etão produtor espera buffer ficar < 100;

Se buffer < 50 então consumidor espera buffer > 500 ou produtor.fimDoArquivo();

Alguém tem alguma ideia/ dica ?

Obrigado,

Marcelo Gomes

9 Respostas

drigo.angelo

Cara, não tenho muita experiência em threads, (e posso estar falando abobrinha) mas, lendo seu problema me veio a cabeça os métodos wait() e notify() da classe Object… deve haver alguma solução padrão para esse tipo de problema, mas imagino que utilize esses métodos
[me corrijam se estiver errado, por favor]

M

Nas minhas tentativas de usar o wait() e o notifiy() percebi (pode ser que fiz alguma coisa errada, se sim por favor me avisem) que o ao entrar no wait ele espera que a thread seja finalizada para continuar e este não é o efeito que eu quero, pelo seguinte:

Se o produtor produzir muito e entrar em wait() {

o consumidor vai consumir o buffer todo e entrar em wait() agurando o produtor ler mais linhas, mas como nenhum dos dois terminou de rodar fica uma esperando o outro;

}
Se o consumidor ler mais que o produtor e entrar no wait(){

o consumidor vai ficar espreando o produtor terminar, não vou ter memória para manter todas as linhas em memória e o sistema vai dar erro por falta de memória

}
otaviojava

Dá uma olhada neste link: http://www.guj.com.br/java/57403-metodo-synchronized-duvida---threadsresolvido

M

Olhei ainda não consegui fazer… vou postar um código que simplificado do meu problema…

Neste caso tenho um buffer que pode ir de 0 a 100, ao chegar a 100 o produtor para de produzir o consumidor, consome toda a produção e o sistema finaliza. enquanto o produtor estiver produzindo o consumidor também esta consumindo.

vamos ao codigo

Main.java

public static void main(String[] args) {
        Buffer buffer = new Buffer();
        
        Produtor produtor       = new Produtor(buffer);
        Consumidor consumidor   = new Consumidor(buffer);

        Thread tProdutor    = new Thread(produtor);
        Thread tConsumidor  = new Thread(consumidor);

        
        tProdutor.start();
        tConsumidor.start();
        



        try {
            tProdutor.join();
            tConsumidor.join();
        } catch (InterruptedException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        System.out.println("Fim");

    }

Produtor.java

public class Produtor implements Runnable{
    private Buffer buffer;

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

    public void run(){
        while(buffer.getContador() < 100){
                buffer.AddWork();
        }
        buffer.StopWork();
    }

}

Consumidor.java

public class Consumidor implements Runnable{
    private Buffer buffer;

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

    public void run() {
         while(buffer.isWork()){
             buffer.DellWork();
         }
    }
}

Buffer.java

public class Buffer {

    private int contador;
    private Boolean work;

    public Buffer() {
        contador = 0;
        work = true;
    }

    public void AddWork() {
        contador++;
        System.out.println("Produzido: " + contador);
    }

    public void DellWork() {
        if (contador != 0) {
            contador--;
            System.out.println("\tConsumidor: " + contador);
        }
        else {
                System.err.println("Consumidor consumindo processador desnecessario");
        }

    }

    public int getContador() {
        return contador;
    }

    public Boolean isWork() {
        if (!work) {
            if (contador == 0) {
                return false;
            }
        }
        return true;
    }

    public void StopWork() {
        this.work = false;
    }
}

Repare que ao rodar este código podemos ver o consumidor tentando consumir várias vezes mesmo sem ter nada no buffer, isso é o ponto que gostária de corrigir.

M

Agora mudo o Buffer.java para fazer com que a produção e o consumo nunca pare…

Buffer.java

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package produtorconsumidor2;

import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author marcelo
 */
public class Buffer {

    private int contador;
    private Boolean work;

    public Buffer() {
        contador = 0;
        work = true;
    }

    public synchronized void AddWork() {
        if (contador > 50) {
            contador++;
            System.out.println("Produzido: " + contador);
        } else {
            while (contador > 20) {
                try {
                    wait();
                } catch (InterruptedException ex) {
                    Logger.getLogger(Buffer.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            notifyAll();
        }
    }

    public synchronized void DellWork() {
        if (contador != 0) {
            contador--;
            System.out.println("\tConsumidor: " + contador);
        } else {
            while (contador < 10) {
                try {
                    wait();
                } catch (InterruptedException ex) {
                    Logger.getLogger(Buffer.class.getName()).log(Level.SEVERE, null, ex);
                }
                System.err.println("Consumidor consumindo processador desnecessario");
            }
            notifyAll();
        }


    }

    public int getContador() {
        return contador;
    }

    public Boolean isWork() {
        if (!work) {
            if (contador == 0) {
                return false;
            }
        }
        return true;
    }

    public void StopWork() {
        this.work = false;
    }
}

Veja que em pouco tempo o produtor fica esperando o consumidor finalizar a thread para continuar e o consumidor fica esperando o produtor finalizar a thread para continuar e o programa fica rodando enternamente, mas sem produzir ou consumir.

Alguma dica ???

E

http://download.oracle.com/javase/6/docs/api/java/util/concurrent/ArrayBlockingQueue.html

M

Nos testes iniciais com ArrayBlockingQueue não funcionou, na verdade no meu teste inicial ele bloqueia inserir quando a fila esta cheia e não permite remover quando a fila esta vazia, mas quando tenta inserir e a fila esta cheia e ele não consegue ele apenas da um exception!!!

bom mas procurando exemplos de como usar este cara cai na página

http://docs.google.com/viewer?a=v&q=cache:iDtl9dDkWBwJ:www.cin.ufpe.br/~wst/Curso/Programa%E7%E3o%20Concorrente%20-%20Aula%208.pdf+ArrayBlockingQueue&hl=pt-BR&gl=br&pid=bl&srcid=ADGEESiiFti4qcyzHVNhPsNBbdmgyf7EBQYnQ-JAUe3cCf1q4VBHcxVvTxJZtKD81iJYXLiXggFl_YQkCmb4lGK6kzpCjmgqRY6nZbq9ofWMwgbxPbsKdEHk4ShEkoD4PYXgEmPIM_Ys&sig=AHIEtbSA0L-vnYxCAGaeHZmozjQePzX6Kg

que tem um exemplo que do wait e notify que acho descobri onde erre… estou refazendo para ver se funciona em breve posto se deu certo ou não.

Obrigado,

marcelo Gomes

E

marcelogomesrp:
Nos testes iniciais com ArrayBlockingQueue não funcionou, na verdade no meu teste inicial ele bloqueia inserir quando a fila esta cheia e não permite remover quando a fila esta vazia, mas quando tenta inserir e a fila esta cheia e ele não consegue ele apenas da um exception!!!

Ainda insisto em que você use um ArrayBlockingQueue. É que você tem de usar offer com timeout ( javadoc ) para pôr na fila - ou seja, para você montar o seu produtor, e poll com timeout ( javadoc ) para você pegar coisas da fila - ou seja, para montar o consumidor.

M

Entanglement,

Vou testar com o ArrayBlockingQueue também, obrigado pela dica.

Agora esta tudo funcionando (ainda sem o arrayBlockingQueue), mas a velocidade esta ruim, pois como ficou tudo sincronizado ele fica mais lento que se tiver rodando sem thread.

Estou lendo mais sobre isso em breve posto como ficou o código completo (com o ArrayBlockingQueue), enquanto isso toda dica é bem vinda.

Obrigado,

Marcelo Gomes

Criado 3 de janeiro de 2011
Ultima resposta 4 de jan. de 2011
Respostas 9
Participantes 4