Duvida thread

5 respostas
giselezr

oi pessoal

estou com uma dúvida em uma questão de thread (achei um topico sobre o mesmo assunto mas não consegui entender http://www.guj.com.br/java/105366-duvida-questao-thread---testkiller)

public class NameList {

    private List names = new ArrayList();

    public synchronized void add(String name) {
        names.add(name);
    }

    public synchronized void printAll() {
        for (int i = 0; i < names.size(); i++) {
            System.out.print(names.get(i) + "");
        }
    }

    public static void main(String[] args) {
        final NameList sl = new NameList();
        for (int i = 0; i < 2; i++) {
            new Thread() {

                public void run() {
                    sl.add("A");
                    sl.add("B");
                    sl.add("C");
                    sl.printAll();
                }
            }.start();
        }
    }
}

A. An exception may be thrown at runtime.
B. The code may run with no output, without exiting.
C. The code may run with no output, exiting normally.
D. The code may rum with output ?A B A B C C ?, then exit.
E. The code may rum with output ?A B C A B C A B C ?, then exit.
F. The code may ruin with output ?A A A B C A B C C ?, then exit.
G. The code may ruin with output ?A B C A A B C A B C ?, then exit.
Answer: EG

a letra E eu entendi, foi a saida que deu, mas não entendi a G… alguem pode me explicar?

abraço

5 Respostas

rogeriopaguilar

Bom, você tem duas threads executando paralelamente e dois métodos sincronizados (add e printAll). Como estes métodos são sincronizados então apenas uma thread pode estar executando eles, a outra thread deve aguardar até que a primeira saia do método sincronizado para poder começar a executar este método.
Imagine então que a primeira thread consiga chamar add(“A”), add(“B”) e add(“C”) mas antes de chamar o método printAll a segunda thread adquira o lock do objeto e consiga chamar add(“A”). Logo depois dessa chamada executada pela segunda thread, a primeira consegue de novo o lock do objeto e chama printAll, então neste momento teremos na lista os itens A, B e C da primeira thread e o A da segunda. Depois que a primeira thread termina de imprimir, a segunda adiciona o B, o C e chama printAll. Neste momento teremos os itens A, B e C da primeira e de novo A, B e C da segunda thread.
Nesta situação, quando a primeira thread chamar printAll ela irá imprimir:

A, B, C, A (este último A foi colocado pela segunda thread)

e quando a segunda thread chamar printAll ela irá imprimir:

A, B, C, A, B, C

rogeriopaguilar

Para que isso ocorra, é neccessário que uma certa ordem ocorra nas operações:

1 - thread 0 adiciona A, B e C e fica esperando a  thread 1 adicionar o A;

2 - thread 1 adiciona A e fica esperando a thread 0 imprimir os dados antes de adicionar o B e o C;

3 - thread 0 imprime os dados;

4 - thread 1 adiciona B e C;

5 - thread 1 imprime os dados

É difícil fazer com que a ordem acima seja satisfeita só com o scheduler utilizado. Eu modifiquei o código para simular esta situação.
Talvez ajude a entender:

package teste.concorrencia;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

public class NameList {

    private List names = new ArrayList();

    private static final CountDownLatch cond1 = new CountDownLatch(1);
    private static final CountDownLatch cond2 = new CountDownLatch(1);
    private static final CountDownLatch cond3 = new CountDownLatch(1);
    
    public synchronized void add(String name) {
        names.add(name);
    }

    public synchronized void printAll() {
        for (int i = 0; i < names.size(); i++) {
            System.out.print(names.get(i) + "");
        }
    }

    public static void main(String[] args) {
        final NameList sl = new NameList();
        
        class TesteThread implements Runnable {
        	int i;
        	
        	public TesteThread(int i ) {
        		this.i = i;
        	}
        	

            public void run() {
            	if( i == 1) {
            		//Se for a segunda thread, aguardar até que a primeira thread tenha adicionado a,b e c
            		try {
						cond2.await();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
            	}
                sl.add("A");
            	if( i == 1) {
            		//Se for a segunda thread, avisa a primeira thread que o a foi adicionado
               		cond1.countDown();
                	try {
                		//espera a primeira thread imprimir, neste momento teremos A, B , C e A an lista
						cond3.await();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
            	}
                sl.add("B");
                sl.add("C");
                if( i == 0) {
                    try {
                    	//Se for a primeira thread, avisa a segunda que  A, B e C foram adicionados e espera a segunda thread adicionar o A
                    	cond2.countDown();
						cond1.await();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} 
                }
                sl.printAll();
                if( i == 0) {
                	//Se for a primeira thread, avisa a segunda que os valores foram impressos
                	cond3.countDown();
                }
            }
        	
        }
        
        for (int i = 0; i < 2; i++) {
            new Thread(new TesteThread(i)).start();
        }
        
        
    }
}
giselezr

oi Rogerio, eu entendi o fluxo, obrigada pela explicação, mas se o comportamento não é garantido, se uma thread pode interferir na outra na hora de adicionar, o que define a letra D e F como erradas?

rogeriopaguilar

Oi Gisele, como vai? Já faz tempo que respondi isso, achei que minha explicação não tinha sido clara o suficiente, rs.

Quanto ao que você perguntou, na letra D até existe uma ordem das operações que faz com que, no final, a lista contenha os itens na ordem A B A B C C (quando eu escrevo “perde o direito” significa que o scheduler interrompe a execução da thread corrente para que outra possa ser executada e quando eu escrevo “ganha a chance” significa que a thread acabou de ser escolhida pelo scheduller para poder executar):

1 - thread 0 adiciona A e B e “perde o direito” de execução;

2 - thread 1 “ganha a chance” de executar, adiciona A e B e “perde o direito” de execução;

3 - thread 0 “ganha a chance” de executar e continua de onde parou, ou seja, antes de adicionar o C na lista. A thread adiciona o C na lista mas antes de chamar o método printAll, “perde o direito” de execução novamente;

4 - thread 1 “ganha a chance” de executar e continua de onde parou, ou seja, antes de adicionar o C na lista. A thread adiciona o C na lista mas antes de chamar o método printAll, “perde o direito” de execução novamente;

neste momento teremos na lista os itens A B A B C C, porém o método printAll sera chamado pelas duas threads, então a sequência acima seria impressa duas vezes e não uma e a saída seria A B A B C C A B A B C C

Já a letra F está errada porque temos duas threads que inserem as letras A, B e C na lista e, independente da ordem que estas inserções ocorrem, o máximo de itens repetidos que ocorrem de forma consecutiva na lista para cada letra será duas vezes, já que cada thread insere cada letra apenas uma vez na lista e existem duas threads executando. Por conta deste fato, a sequência inicial “A A A” nunca irá ocorrer. O que poderia ocorrer é termos uma sequência inicial “A A”, onde cada A foi inserido por uma das duas threads, porém o terceiro item seria um B e nunca um A novamente.

Se você for fazer a prova de certificação irá se deparar com alguma questão assim. Quando eu fiz eu lembro que deixei a questão para o final e quando voltei a ela já sabia que tinha passado no exame pelas outras questões que eu fiz, então eu chutei a resposta desta questão porque se fosse ter que analisar cada alternativa ficaria a prova inteira só pra resolver esta questão.
Aliás, é sacanagem eles colocarem uma questão destas na prova, hehehe…

[]'s

giselezr

oi Rogerio, ótima explicação!
muuuuuuuuuuuito obrigada :smiley:

Criado 9 de outubro de 2012
Ultima resposta 17 de jan. de 2013
Respostas 5
Participantes 2