Exemplo de uso de sincronização (clássico, faço isso toda vez :lol: ).
Aplicação de banco.
public class Conta{
private Double saldo;
public boolean sacar(double quantia){
if(saldo.doubleValue()>=quantia){
saldo = Double.valueOf(saldo.doubleValue()-quantia);
return true;
}
return false;
}
public void depositar(double quantia){
saldo = Double.valueOf(saldo.doubleValue()+quantia);
}
}
Agora imagine que João e Maria tem uma conta conjunta, cada um tem um cartão e pode executar saques e depósitos individualmente.
O saldo da conta deles é de R$ 1.000,00.
Maria vai ao caixa eletrônico para sacar R$ 600,00, ao digitar os dados o servidor inicia uma Thread para realizar o saque.
João chega ao mesmo tempo em outro caixa eletrônico para fazer um saque de R$ 500,00, ao digitar os dados o servidor inicia uma Thread para realizar o saque.
Agora o servidor tem 2 Threads para usar o mesmo objeto Conta que o servidor tem em memória...
A Thread de Maria começa a executar primeiro, executa o if e verifica se o saldo é maior ou igual a quantia a ser sacada, como o saldo é de R$ 1.000,00 e a quantia é de R$ 600,00 entra no if para subtrair o valor do saldo, mas antes de subtrair o sistema operacional pára a Thread de Maria para executar a de João...
A Thread de João começa a executar, executa o if e verifica se o saldo é maior ou igual à quantia a ser sacada, como o saldo é de R$ 1.000,00 (lembre-se que a Thread de Maria parou antes de subtrair) e a quantia é R$ 500,00 entra no if e subtrai o valor do saldo, retorna true e o caixa eletrônico libera o dinheiro... A Thread de João morre...
A Thread de Maria volta a executar de onde parou, subtrai os R$ 600,00 do saldo e retorna true, o caixa eletrônico então libera o dinheiro e a Thread de Maria morre...
Pronto, agora o saldo da conta é de -R$ 100,00, pois sua aplicação permitiu um saque de R$ 1.100,00 em uma conta que só tinha saldo de R$ 1.000,00.
Agora modificando o código...
public class Conta{
private Double saldo;
public boolean sacar(double quantia){
synchronized(saldo){
if(saldo.doubleValue()>=quantia){
saldo = Double.valueOf(saldo.doubleValue()-quantia);
saldo.notifyAll();
return true;
}
return false;
}
}
public void depositar(double quantia){
synchronized(saldo){
saldo = Double.valueOf(saldo.doubleValue()+quantia);
saldo.notifyAll();
}
}
}
Analisando a mesma situação com código sincronizado.
Maria vai ao caixa eletrônico para sacar R$ 600,00, ao digitar os dados o servidor inicia uma Thread para realizar o saque.
...
A Thread de Maria começa a executar primeiro, executa o if e verifica se o saldo é maior ou igual a quantia a ser sacada, como o saldo é de R$ 1.000,00 e a quantia é de R$ 600,00 entra no if para subtrair o valor do saldo, mas antes de subtrair o sistema operacional pára a Thread de Maria para executar a de João...
A Thread de João começa a executar, tenta acessar o objeto "saldo", mas como ele está travado por outra Thread, a Thread de João aguarda...
A Thread de Maria volta a executar de onde parou, subtrai os R$ 600,00 do saldo e retorna true, o caixa eletrônico então libera o dinheiro, Thread de Maria notifica todas as Threads que estava esperando aquele objeto e morre...
A Thread de João volta a executar, já que o objeto "saldo" não está mais bloqueado, executa o if, e como o saldo da conta é inferior à quantia a ser sacada, ela retorna false e o caixa eletrônico notifica o cliente de que o saldo é insuficiente, a Thread de João morre...
Esse é um caso simples do uso de sincronismo...
As questões sobre a posição do modificador, e de que tipo de objeto pode ser usado, já foi explicado nas outras respostas...
:wink: