Threads e Synchronized

Pessoal to com uma dúvida cruel,

Estou fazendo um método de exportação de uma imagem para pdf, só que ele está exportando antes de a imagem ficar montada, exportando-a pela metade, a lógica q eu sigo é a seguinte

public void makePDF()
{
            
                Object obj = new Object();

                // aqui ele chama varios procedimentos seguidos por várias threads que demoram uns 3 segundos até montar toda a estrutura que depois será convertida em uma imagem.. aqui que está o problema, eu quero que ele siga adiante, sometne quando isto tiver concluido.
                obj.setValues( xxx );

                // a imagem vem pela metade pois ainda existem threads sendo executadas acima..
                preview = getImage( obj );

O que eu poderia fazer para resolver o caso?

Vlw,

Boas festas a todos

Pelo que entendi vc precisa que seu método espere as outras threads terminarem o trabalho delas nao e isso?

Se for isso mesmo, o método join() serve pra isso ^^

http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Thread.html#join()

Posta o seu código completo do método para podermos analisar melhor
Mas é isso mesmo utilize o método join() da thread para poder esperar

public class Exemplo extends Thread  {


	public void run(){
		for (int i = 0; i < 5000; i++) {
			System.out.println(i + ". Thread Executando");
			
		}
	}

	public static void main(String[] args) {
		Exemplo e1 = new Exemplo();

		e1.start();
		try {e1.join();}catch(Exception e){e.printStackTrace();}
		for (int i = 0; i < 5000; i++) {
			System.out.println(i + ". Main Executando");
			
		}
		
		System.out.println("Fim da main");
	}
}

Vlw pelas respostas…

O problema é que eu “não” tenho acesso as threads daquele metodo, é uma estrutura complexa que acontece apartir daquele metodo, são varias threads que startam… se fosse uma unica eu até tirava ela :p,

Ta complicado de fazer isso tudo entrar na linha hehe,

Mas se alguem tiver alguma ideia a mais posta ai,…
Vou ler mais sobre o metodo join()…
Vlw pessoal

Cara, vc pode usar o join com qnts Threads vc quiser…

O problema é que eu não tenho acesso às threads, e não posso modificar a estrutura daquele set, pq ele é utilizado em n outros lugares, tenho que adaptar meu código aquele metodo,

Vou postar o código em sí pra voces darem uma olhada:

public void makePDF( final ProgressListener progressListener )
    {
        try
        {
	    int count = 0;
		
            for ( Slide slide : slides )
            {
                if ( progressListener != null )
                {
                    progressListener.progressChanged( new ProgressEvent( this, slide.getName() ) );
                }

                Presenter presenter= new Presenter();

                // São varios procedimentos que são chamados nesse método, não posso alterar, e não tem controle sobre as threads
                presenterPane.setCurrentSlide( slide );
                
                try
                {
                    // Aqui que eu tenho que aguardar.. e usar um Sleep seria "matação"..
                    preview = getImage( presenterPane );

                    if ( imageFactory == null )
                    {
                        imageFactory = new ImageFactory();
                        imageFactory.setImageSize( screenSize );
                    }
                }

                catch (Exception ex)
                {
                    ApplicationContext.getInstance().logException( ex );
                }

                preview = imageFactory.getImage( presenterPane );

                Image i = Image.getInstance( preview, Color.BLACK );

                if ( i != null )
                {
                    document.add( i );
                }

                if ( count % MAX_SLIDES == 0 )
                {
                    System.gc();
                }

                count++;
            }
        }
        
        catch ( Exception e )
        {
            ApplicationContext.getInstance().logException( e );
        }

       
        document.close();
        document = null;
    }

Classe Presenter:

public class Presenter
    extends JPanel
{
....

public void setCurrentSlide( Slide slide )
    {
        slideStage.setSlide( slide );

// Ele entra aqui, e starta várias threads.. são classes e classes de código..
        registerSlide( slide );
    }
}

Tem como eu acrescentar um tratamento pra aguardar tudo isso, sem mexer nessas classes já existentes?
Não queria utilizar um sleep com tempo estatico.

Vlw

alguma coisa dentro da classe principal das thread vc precisa mexer ( na minha visao ).
eu trataria com barreiras e semaforo.
um semaforo para travar o fluxo principal e uma barreira para que quando todas as threads finalizassem o servico, ela avisaria o semaforo e o fluxo principal voltaria.

por exemplo, vc tem 1000 threads executando alguma coisa.
no final de cada thread voce avisaria a barreira que “acabou o servico”.
cada thread vai passar pela barreira, decrementar uma unidade e se bloquear.
quando a thread ultima thread passar pela barreira, o codigo dentro da declaracao da barreira é executado e tudo continua normalmente.

dá uma olhada em : http://programmingexamples.wikidot.com/cyclicbarrier

vou botar um codigo aqui, nao sei se vai ajudar ou piorar, tá comentado… mas qualquer coisa avise.

import java.awt.image.WritableRaster;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Semaphore;

/* @author Arthur F. Zanona */

    public class ZhangSuen {
        // imagem
        private WritableRaster raster;
        // ponto de parada
        private Boolean houveExclusao = false;
        // controle do termino
        private CyclicBarrier barreira;
        // numero do passo, e ja iniciando no 1
        private Integer passo = 1;
        // controlando fluxo algoritmo
        private Semaphore semaforo = new Semaphore(0);

    public ZhangSuen(WritableRaster raster) {
        this.raster = raster;
        // controlando as threads
        this.barreira = new CyclicBarrier( raster.getHeight() - 4, new Runnable() {
                // qdo todas as threads estiverem prontas, eu libero o fluxo principal
                public void run() {
                    semaforo.release();
                }
            });
    }

        private synchronized void houveExclusao() {
            houveExclusao = Boolean.TRUE;
        }

        public void execute(  ) {
            houveExclusao = Boolean.FALSE;
            do {
                houveExclusao = Boolean.FALSE;
                // qdo criar todas as threads ele inicia
                for (int i = 1; i < raster.getHeight() - 3; i++) {
                    ThreadChines thread = new ThreadChines( i );
                    thread.start();
                }
                // aguardando todas as threads marcarem os pixels para exclusao em toda a imagem
                try {
                    semaforo.acquire();
                    // como ja excluiu, muda o passo
                    passo = passo == 1 ? 2 : 1;
                    // reiniciando a barreira
                    barreira.reset();
                } catch (InterruptedException ex) {System.out.println( ex.getMessage() ); }
                // fica nessa até nao houver nada a excluir
            } while ( houveExclusao );
        }

        public class ThreadChines extends Thread {        
        // linha do processamento
        private Integer altura;
        // os excluidos
        private List<int[]> excluidos = new ArrayList<int[]>();

        public ThreadChines(Integer altura) {
            this.altura = altura;            
        }


        @Override
        public void run() {
            super.run();
            for ( int largura = 1 ; largura < raster.getWidth() - 3 ; largura++ ) {

                Thread.yield();

                // declaracao
                int[] p1 = new int[3], p2 = new int[3], p3 = new int[3],
                      p4 = new int[3], p5 = new int[3], p6 = new int[3],
                      p7 = new int[3], p8 = new int[3], p9 = new int[3];

                int conectividade = 0;
                int vizinhosPretos = 0;
                // resgatando os pixels
                raster.getPixel(largura - 1, altura - 1, p9);
                raster.getPixel(largura - 1, altura, p8);
                raster.getPixel(largura - 1, altura + 1, p7);
                raster.getPixel(largura, altura + 1, p6);
                raster.getPixel(largura + 1, altura + 1, p5);
                raster.getPixel(largura + 1, altura, p4);
                raster.getPixel(largura + 1, altura - 1, p3);
                raster.getPixel(largura, altura - 1, p2);
                raster.getPixel(largura, altura, p1);

                Thread.yield();
                // conectividade
                if ((p1[0] == 0) && (p2[0] == 255) && (p3[0] == 0)) conectividade++;
                if ((p1[0] == 0) && (p3[0] == 255) && (p4[0] == 0)) conectividade++;
                if ((p1[0] == 0) && (p4[0] == 255) && (p5[0] == 0)) conectividade++;
                if ((p1[0] == 0) && (p5[0] == 255) && (p6[0] == 0)) conectividade++;
                if ((p1[0] == 0) && (p6[0] == 255) && (p7[0] == 0)) conectividade++;
                if ((p1[0] == 0) && (p7[0] == 255) && (p8[0] == 0)) conectividade++;
                if ((p1[0] == 0) && (p8[0] == 255) && (p9[0] == 0)) conectividade++;
                if ((p1[0] == 0) && (p9[0] == 255) && (p2[0] == 0)) conectividade++;

                // vizinhos pretos
                if ((p2[0] == 0) && (p3[0] == 0)) vizinhosPretos++;
                if ((p3[0] == 0) && (p4[0] == 0)) vizinhosPretos++;
                if ((p4[0] == 0) && (p5[0] == 0)) vizinhosPretos++;
                if ((p5[0] == 0) && (p6[0] == 0)) vizinhosPretos++;
                if ((p6[0] == 0) && (p7[0] == 0)) vizinhosPretos++;
                if ((p7[0] == 0) && (p8[0] == 0)) vizinhosPretos++;
                if ((p8[0] == 0) && (p9[0] == 0)) vizinhosPretos++;
                if ((p9[0] == 0) && (p2[0] == 0)) vizinhosPretos++;
                //System.out.println("Conectividade" + conectividade );
                //System.out.println("Vizinhos pretos" + vizinhosPretos );
                boolean condicaoA = conectividade == 1,
                        condicaoB = vizinhosPretos >= 2 && vizinhosPretos <= 6,
                        condicaoC = passo == 1 ? (p2[0] == 255 || p4[0] == 255 || p8[0] == 255) : (p2[0] == 255 || p4[0] == 255 || p6[0] == 255),
                        condicaoD = passo == 1 ? (p2[0] == 255 || p6[0] == 255 || p8[0] == 255) : (p4[0] == 255 || p6[0] == 255 || p8[0] == 255);
                //System.out.println("CondicaoC" + condicaoC );
                //System.out.println("CondicaoD" + condicaoD );
                if (condicaoA && condicaoB && condicaoC && condicaoD) {
                            excluidos.add( new int[]{largura, altura} );                            
                }



            }
            if ( excluidos.size() > 0 ) houveExclusao();
            Thread.yield();
            try {
                // avisa que acabou
                barreira.await();
                for (int[] e : excluidos) {
                    raster.setPixel(e[0], e[1], new int[]{255, 255, 255});
                }
                excluidos.clear();
            }
            catch (InterruptedException ex) {System.out.println( ex.getMessage() ); }
            catch (BrokenBarrierException e) {System.out.println( e.getMessage() ); }


        }



    }
}

Me parece só deixar o método makePdf syncronized já resolveria o problema, não?

syncronized apenas garante que uma thread vai executar aquele trecho por vez.

Exatamente. E pelo que ele descreveu do problema, parece que só isso mesmo é necessário.

Ah não, viajei. Realmente, sem modificar aquele set, não há muito o que você possa fazer, a não ser dar um Thread.sleep() antes do comando. Naquele trecho de código, se você não pode saber que threads existem, não tem como esperar por nada.

E, infelizmente, o sleep não será garantido. Basta ter um pdf longo, e sua solução foi pro brejo.

Essa classe Image aí… é a Image padrão do Java?

Se for, geralmente é possível verificar se uma imagem foi completamente carregada através do MediaTracker.
Veja um exemplo aqui.

É sim uma imagem do java, mas não posso usar media tracker pq como ele é um componente com base em um jpanel, a imagem vai sempre estar gerada certo, so que não completa pq as threads ainda estão montando…

Pessoal, e se eu usar

Thread.getAllStackTraces(); (Returns a map of stack traces for all live threads)

Será que não da pra verificar qual a thread que veio de tal component ( é tudo bem gerenciado ) e aguardar aquelas threads especificas, pq o fato é que eu tenho que encontrar as threads que eu devo esperar, certo?!,

O sleep seria matação, e como o ViniGody disse, quando tiver muito dado, já vai dar pau…, ( assim era feito até hoje )

a.zanona, vlw pela dica, vou ver o impacto que dá no resto do código…

Alguém tem mais uma idéia mirabolante?

Vocês já tentaram passar fica durex ao redor de um cano furado?
hehe

Eu fiz isso pra resolve meu problema,… então fiz uma baita matação, essencialmente, ela funciona, mas vou ter que pensar em outras formas mais inteligentes,

Mas dão uma olhada no que eu fiz, e opinem…

fiz o chamamento deste metodo, antes de ele gerar a imagem, o intenção dele, é ficar rodando até todas as threads com tags sobre o tema que ele ta processando finalizarem…

public void waitToProcess() throws InterruptedException
{
    List<Thread> waiting = new ArrayList<Thread>();

        Map<Thread, StackTraceElement[]> a = Thread.getAllStackTraces();

        for ( Thread t : a.keySet() )
        {
            StackTraceElement[] el = a.get( t );

            for ( StackTraceElement d : el )
            {
                if ( d.getClassName().equals( new PresenterApplication().getClass().getName() ) )
                {
                    System.out.println( d.getClassName() );
                    System.out.println( d.getFileName() );
                    System.out.println( d.getMethodName() );
                    System.out.println( "" );

                    if ( ! waiting.contains( t ) )
                    {
                        waiting.add( t );
                    }
                }
            }
        }

    boolean stillProcessing = true;

    while ( stillProcessing )
    {
        stillProcessing = false;

        for ( Thread th : waiting )
        {
            if ( th.isAlive() && th.getState() == State.RUNNABLE )
            {
                stillProcessing = true;
            }
        }
    }

// verifica se existem subthreads que outras threads startaram.. ( a estrutura pode ficar realmente grande )
    boolean thereIsMore = false;

    Map<Thread, StackTraceElement[]> ab = Thread.getAllStackTraces();

    for ( Thread tb : ab.keySet() )
    {
        StackTraceElement[] elb = ab.get( tb );

        for ( StackTraceElement d : elb )
        {
            if ( d.getClassName().equals( new PresenterApplication().getClass().getName() ) && tb.getState() == State.RUNNABLE )
            {
                System.out.println("There is more threads.. owwww");
                thereIsMore = true;
            }
        }
    }

// fica no looping até todas as threads estarem ok
    if ( thereIsMore )
    {
       waitToProcess();
    }
}

Como eu comentei, funcionou, mas tapei o sol com a peneira…

Assim que tiver mais um tempo, volto pra este codigo e penso em mais algo, e posto aqui,
Mas se alguem tiver alguma solução, (sem mudar o set inicial) posta aqui…

Vlw pela ajuda pessoal… =D

Só tenho uma coisa a dizer: medo.

Mas pelo menos você tá ciente de que essa é uma solução silver tape… :lol:

puxa :S

hehehe
é como eu disse tapei o cano com fita durex ehhehe :smiley: