[code]public class TestSeven extends Thread
{
private static int x;
public synchronized void doThings(){
int current = x;
current++;
x = current;
}
public void run(){
doThings();
}
}[/code]
Which statement is true?
A. Compilation fails.
B. An exception is thrown in runtime.
C. Synchronizing the run() method would make the class thread-safe.
D. The data in variable “x” are protected from concurrent access problems.
E. Declaring the doThings() method as static would make the class thread-safe.
F. Wrapping the statements within doThings() in a synchronized(new Object()){} block would make the class thread-safe.
A resposta correta é a E. A minha pergunta é: ao fazer o método doThings() ficar estático, eu estou automaticamente sincronizando os campos estáticos também? Porque eu só concordo com essa resposta se a palavra synchronized no métod doThings() sincronizar também a variável static int x.
Esse exemplo mostra porque coisas synchronized são uma péssima escolha quando o assunto é thread safety.
O que acontece quando o trecho é sincronizado, mas não estático? Duas threads, rodando paralelamente, poderão acessar aquele trecho de código ao mesmo tempo, em objetos diferentes. Porém, ambas estarão compartilhando a variável x.
A solução para isso é que ambas as threads compartilhem o monitor. Para fazer isso, você declara o método como static (que fará com que compartilhem o monitor TesteSeven.class). Ou, no interior do método faz
public void doThings(){
synchronized (x) {
int current = x;
current++;
x = current;
}
}
Para que ambas compartilhem x.
Uma última alternativa seria criar um lock externo, e fazer com que as threads o compartilhem:
[code]public class TestSeven extends Thread
{
private int[] lock;
private static int x;
public TestSeven(int[] lock) {
this.lock = lock;
}
public void doThings(){
synchronized (lock) {
int current = x;
current++;
x = current;
}
}
public void run(){
doThings();
}
}[/code]
Isso seria usado assim:
int[] lock = new lock[0];
TestSeven one = new TestSeven(lock);
TestSeven two = new TestSeven(lock);
Agora, não entendi o que você quer dizer com “sincronize a variável x”. O que é sincronizado é a área de código, não variáveis. A sincronização faz com que duas threads não possam acessar o mesmo trecho de código do mesmo objeto ao mesmo tempo.
Apenas quando o método é estático. Aí vc não tem o this.
Quando o método não é estático, o sincronismo é feito em this. Então, se duas threads forem disparadas por objetos diferentes, o que acontece? Você tem dois “this” diferentes (cada um de um objeto), mas uma única variável x, já que ela é estática.
Humm, perfeito Mais uma dúvida: eu poderia forçar o caso do código (I) ? Ou seja, eu poderia sincronizar um método não estático usando syncronized(NomeDaClasse.class) ??
Note que embora essa seja a alternativa correta do ponto de vista da linguagem, é uma péssima alternativa do ponto de vista de multi-threading. Fazer o método doThings() sincronizado é o mesmo que dizer que apenas uma única thread pode percorre-lo, sempre, independente da classe do objeto.
Nesse caso, você literalmetne irá serializar todas as threads que precisarem do doThings(). E rodar uma thread por vez, ou não ter várias threads, é a mesma coisa.
Fiquei com outra dúvida agora. Caso (naquele primeiro código) a variável static x não fosse private, mas sim public, então não adiantaria nada colocar o método doThings() como static, correto? Pois aí, qualquer classe poderia fazer algo do tipo:
Podendo, assim, quebrar a consistência da váriável x.
Sim, está correto. Outra forma, seria se houvesse um método não-sincronizado acessando x. Qualquer classe que usasse esse método, estaria fazendo caquinha. A regra de ouro é:
Se existe duas ou mais threads alterando uma variável, todo e qualquer acesso a essa variável deve, necessariamente, estar num bloco sincronizado.
[quote=ViniGodoy]Esse exemplo mostra porque coisas synchronized são uma péssima escolha quando o assunto é thread safety.
O que acontece quando o trecho é sincronizado, mas não estático? Duas threads, rodando paralelamente, poderão acessar aquele trecho de código ao mesmo tempo, em objetos diferentes. Porém, ambas estarão compartilhando a variável x.
A solução para isso é que ambas as threads compartilhem o monitor. Para fazer isso, você declara o método como static (que fará com que compartilhem o monitor TesteSeven.class). Ou, no interior do método faz
public void doThings(){
synchronized (x) {
int current = x;
current++;
x = current;
}
}
Para que ambas compartilhem x.
Uma última alternativa seria criar um lock externo, e fazer com que as threads o compartilhem:
[code]public class TestSeven extends Thread
{
private int[] lock;
private static int x;
public TestSeven(int[] lock) {
this.lock = lock;
}
public synchronized void doThings(){
int current = x;
current++;
x = current;
}
public void run(){
doThings();
}
}[/code]
Isso seria usado assim:
int[] lock = new lock[0];
TestSeven one = new TestSeven(lock);
TestSeven two = new TestSeven(lock);
Agora, não entendi o que você quer dizer com “sincronize a variável x”. O que é sincronizado é a área de código, não variáveis. A sincronização faz com que duas threads não possam acessar o mesmo trecho de código do mesmo objeto ao mesmo tempo.[/quote]
Viny, não consegui enxergar o lock onde está acontecendo, poderia por favor me explicar… pois pensei que teria que em algum momento, passar o lock para synchronized.
Valeu Viny, por favor, você teria alguma referência de um bom livro(ou alguns) que tratasse somente de Threads? ja estudei semaforos, e outros assuntos…, e continuo com muita dificuldade, e vejo que você domina o assunto como eu nunca vi, qual foi o caminho para dominar de tal forma?
[quote=“Ponto V”] Java Concurrency in Practice, do Brian Goetz: Um dos melhores livros sobre multi-threading e Java, escrito por ninguém menos do que o criador da linguagem.[/quote]