Problema com bloco sincronizado

7 respostas
Rafael_Mendon

Boa noite para todos.

Estou com um pequeno problema que não consigo resolver.

Criei uma classe que extende Thread e uma outra principal, na principal eu faço um laço para instanciar e startar tres objs threads. Na classe da thread, no método run, eu faço um laço para imprimir de 1 a 10, coloquei o contador como static para todos os objs enxergarem o mesmo contador, sincronizei o obj para o numero não se repitir na thread. Segue o código:

Classe Principal

public class Ex5Main {

	public static void main(String[] args) {

		for (int i = 0; i < 3; i++) {
			Ex5MinhaThread t = new Ex5MinhaThread(i);
			t.start();
		}
		System.out.println("---Fim das 3 Threads disparadas---");
	}
}

Classe da Thread

public class Ex5MinhaThread extends Thread {

	private static int i = 0;
	private int numeroThread;

	public Ex5MinhaThread(int numeroThread) {
		this.numeroThread = numeroThread;
	}

	public void run() {

		while (i < 10) {
			synchronized (this.getClass()) {
				i++;
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("Thread " + numeroThread + ": " + i);
			}
		}
	}
}

Quando rodo o programa acontece algo bizarro, segue a saída:
—Fim das 3 Threads disparadas—
Thread 0: 1
Thread 2: 2
Thread 1: 3
Thread 1: 4
Thread 1: 5
Thread 1: 6
Thread 1: 7
Thread 1: 8
Thread 2: 9
Thread 2: 10
Thread 0: 11
Thread 1: 12

Era para parar no número 10 mas o programa ainda imprimiu 11 e 12, agora quando rodo debugando o problema não acontece:
—Fim das 3 Threads disparadas—
Thread 0: 1
Thread 2: 2
Thread 1: 3
Thread 2: 4
Thread 0: 5
Thread 1: 6
Thread 1: 7
Thread 1: 8
Thread 1: 9
Thread 1: 10

Alguem poderia me ajudar a resolver este problema? Por que não vai ate o 10?

Abs
Rafael

7 Respostas

rogeriopaguilar

Troca o código do método run para:

public void run() {

			while (i < 10) {
				synchronized (this.getClass()) {
					if(i < 10) {
						i++;
						System.out.println("Thread " + numeroThread + ": " + i);
					}
				}
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
	}

[]'s
Rogério

bortolettot

Faça dessa maneira que vai da certo.

public class Ex5MinhaThread extends Thread {

	private static int i = 0;
	private int numeroThread;

	public Ex5MinhaThread(int numeroThread) {
		this.numeroThread = numeroThread;
	}

	public void run() {
		synchronized (this.getClass()) {
		       while (i < 10) {
				i++;
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("Thread " + numeroThread + ": " + i);
			}
		}
	}
}

Com esse código você vai usar uma Thread só, porque as outras threads vão ficar esperando a primeira thread sair do synchronized, quando a primeira Thread sair o i vai ser >= 10

rogeriopaguilar

Sim, é por isso que eu fiz da forma que fiz, para poder utilizar todas as threads, mas eu não sei direito se é isso que ele queria…

rogeriopaguilar

Também é possível resolver isso de uma forma mais simples utilizando as classes atômicas:

import java.util.concurrent.atomic.AtomicInteger;

public class Ex5MinhaThread extends Thread {

	private static AtomicInteger ai = new AtomicInteger(0);
	private int numeroThread;

	public Ex5MinhaThread(int numeroThread) {
		this.numeroThread = numeroThread;
	}

	public void run() {
			int i;
			while( (i = ai.incrementAndGet()) <= 10) {
				System.out.println("Thread " + numeroThread + ": " + i);
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
	}
}
bortolettot

Legal o exemplo que você deu [color=blue]rogeriopaguilar[/color]

Uma outro saída também é trabalhar com get/set synchronized.

O Código ficaria assim:

public class Ex5MinhaThread extends Thread {  
	private static int i = 0;  
	private int numeroThread;  

	public Ex5MinhaThread(int numeroThread) {  
		this.numeroThread = numeroThread;  
	}  

	public void run() {  
		while (getI() < 10) {  
			setI(getI()+1);
			System.out.println("Thread " + numeroThread + ": " + i);
			try {  
				Thread.sleep(1000);  
			} catch (InterruptedException e) {  
				e.printStackTrace();  
			}  
			  
		}  

	}

	public synchronized static int getI() {
		return i;
	}

	public synchronized static void setI(int i) {
		Ex5MinhaThread.i = i;
	} 
}
rogeriopaguilar

Isso, assim também fica legal.
Eu estou lendo um livro bem legal sobre concorrência em java. Se tiverem interesse em se aprofundar no assunto:

Se eu não me engano tem uma versão traduzida também. Os primeiros capítulos podem parecer meio chatos mas vale a pena ler pra entender o resto do livro.

[]'s
Rogério

bortolettot

O livro parece ser bem legal.

Obrigado pela indicação.

Abraços :smiley:

Criado 11 de setembro de 2012
Ultima resposta 15 de set. de 2012
Respostas 7
Participantes 3