Bom dia, estou tentando representar um exercício simples para trabalhar com Threads em Java, hoje tentei implementar um programa que cria 4 Threads e cada uma acessa uma variável compartilhada que faz o incremento da mesma, a intenção é evitar que mais de uma thread acesse essa variável ao mesmo tempo usando a palavra reservada synchronized. Porém quando executo meu código varias vezes, há algumas situações que aparentemente mais de uma thread é acessada ao mesmo tempo. Por exemplo:
saída recebida:
Thread-3 2
Thread-0 1
Thread-1 2
Thread-2 3
pois a saída esperada era:
Thread-3 0
Thread-0 1
Thread-1 2
Thread-2 3
Não necessariamente nessa ordem de thread, mas obrigatoriamente os números em negrito devem ser únicos.
Gostaria de saber porque a palavra reservada synchronized não está surtindo efeito, e o código está executando como se não a tivesse.
Segue o código do programa:
Class ThreadRunnable.java:
package com.threads.implementacao;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ThreadRunnable implements Runnable {
private String nome;
private int tempo;
public static int i = -1;
public ThreadRunnable(String nome, int tempo) {
this.nome = nome;
this.tempo = tempo;
Thread t = new Thread(this);
t.start();
}
@Override
public synchronized void run() {
i++;
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
Class ThreadExecute.java:
package com.threads.implementacao;
public class ThreadExecute {
public static void main(String[] args) {
ThreadRunnable thread1 = new ThreadRunnable("#0", 500);
ThreadRunnable thread2 = new ThreadRunnable("#1", 500);
ThreadRunnable thread3 = new ThreadRunnable("#2", 500);
ThreadRunnable thread4 = new ThreadRunnable("#3", 500);
System.out.println(Thread.currentThread().getName());
}
}
1 curtida
Quando você cria um método synchronized
, ele sincroniza apenas na instância do respectivo objeto. Portanto, isto não se aplica a variáveis estáticas, já que elas pertencem à classe, e não às suas instâncias.
Para sincronizar o acesso à variável estática, você deve fazê-lo na classe, e não no método:
@Override
public void run() {
synchronized (ThreadRunnable.class) {
i++;
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
Ou então você cria um outro objeto estático que servirá como o lock, e faz a sincronização nele:
public class ThreadRunnable implements Runnable {
private String nome;
private int tempo;
public static int i = -1;
private static Object LOCK = new Object();
public ThreadRunnable(String nome, int tempo) {
this.nome = nome;
this.tempo = tempo;
Thread t = new Thread(this);
t.start();
}
@Override
public void run() {
synchronized (LOCK) {
i++;
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
1 curtida
@hugokotsubo entendi agora, faz mais sentido.
Umas dúvidas me surgiram agora…
Eu também posso usar
synchronized (this) {
.
.
.
}
que terá o mesmo efeito certo ??
E outra…
Por que é que quando uso Thread.sleep(100); dentro do run() logo após o incremento as saídas tornam a se bagunçar ? Não era pra entrar apenas 1 thread por vez dentro do bloco que está o synchronized, idependentemente do tempo que eu colocar a thread para aguardar ?
Não. Isso vai sincronizar somente a instância, e não se aplicará às variáveis estáticas.
Se você fez isso com synchronized(this)
, não vai funcionar mesmo, pois como já disse, ele não vai sincronizar o acesso à variáveis estáticas.
Se fizer assim funciona:
public void run() {
synchronized (ThreadRunnable.class) {
try {
System.out.println(Thread.currentThread().getName() + " antes do incremento");
i++;
System.out.println(Thread.currentThread().getName() + " antes do sleep");
Thread.sleep(1000L);
System.out.println(Thread.currentThread().getName() + " depois: " + i);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
Saída:
Thread-0 antes do incremento
Thread-0 antes do sleep
Thread-0 depois: 0
Thread-3 antes do incremento
Thread-3 antes do sleep
Thread-3 depois: 1
Thread-2 antes do incremento
Thread-2 antes do sleep
Thread-2 depois: 2
Thread-1 antes do incremento
Thread-1 antes do sleep
Thread-1 depois: 3
Repare que cada thread executa todo o bloco synchronized
, inclusive o sleep
.
1 curtida
entendi, muito obrigado !!!