Sincronização:
* O principio desse capitulo é falar sobre o acesso de múltiplas threads ao mesmo OBJETO, ou seja, 2 chamadas de métodos/instruções em 2 stacks diferentes porem os métodos/instruções são do mesmo objeto.
- QUANDO OCORRE TIMESLICING EM THREADS QUE NÃO ACESSAM OS MESMOS OBJETOS, O PC DO PC REGISTER GARANTE QUE TODAS AS INTRUÇÕES SERÃO FEITAS. NO CASO DE 2 THREADS COM TIMESLICING NO MESMO OBJETO, o pc do PC register garante que todas instruções serão feitas, porem o problema é em qual instrução estamos.
Podemos considera-lá como um átomo, não pode ser dividida.
Descrevendo uma situação provável:
Uma objeto chamado Object, que contem um método chamado somar e outro chamado subtrair, a thread A deve invocar somar e a thread B deve invocar subtrair, porem ambas elas estão FAZENDO NO MESMO OBJETO...Lembre-se muitas vezes o escalonador não tem uma ordem certa com threads de mesma prioridade, Portanto mesmo que você faça primeiro
ThreadA.somar();
ThreadB.subtrair();
Pode ser que ocorra isso: Um timeSlicing de threads, vamos ver:
public int x = 5;
Thread A - Prioridade 5 - Incremente o valor em 5,acessando o método do objeto que praticamente faz isso:
x = x + 5;
Thread B - Prioridade 5 - Decrementa o valor em 5, acessando o método do objeto que praticamente faz isso:
x = x - 5;
Object - as Duas Threads tem o mesmo objeto como RUNNABLE
getfield #2; = Instance variable que é uma referencia dinâmica a constant Pool da classe, que sera resolvida depois...Ou seja instance variable do Objeto
VAMOS VER OQUE ACONTECERÁ:
*Thread A - usa uma parte do TimeSlicing dela, invocando somar() no Object
getfield #2
iconst_5
iadd <----
putfield #2
Quando o pc do pc register da method area estava na instrução iconst_5 <-, o tempo se esgotou da THREAD A, portanto é hora da THREAD B.(Lembre-se que o <--- mudou porque quando o pc do Pc register faz uma instrução ele salva o estado e guarda o endereço da próxima instrução)
A Operand STACK referente a Thread A esta:
5
5
---------x-----------
*Thread B - usa uma parte do TimeSlicing dela, invocando subtrair() no Object(Mesmo Objeto que A)
getfield #2
iconst_5
isub <----
putfield #2
O pc do pc register da method area estava na instrução iconst_5 <-, o tempo se esgotou da THREAD B, portanto é hora da THREAD A.(Lembre-se que o <--- mudou porque quando o pc do Pc register faz uma instrução ele salva o estado e guarda o endereço da próxima instrução)
A Operand STACK ESTA
5
5
----------x----------
*Thread A - usa uma parte do TimeSlicing dela, invocando somar() no Object
getfield #2
iconst_5
iadd
putfield #2
-------->Termino da Frame.
A Operand STACK ESTA - sem nada agora, x = 10 agora.
--------x----------
*Thread B -usa uma parte do TimeSlicing dela, invocando subtrair() no Object(Mesmo Objeto que A)
Repare que, o valor de x é 10, não é mais 5, porem a atualização não acontece aqui, pois a instrução de carregar o valor(getfield #2) foi feita antes da instrução de adicionar(iadd (x = x + 5)) da THREAD A
getfield #2
iconst_5
isub
putfield #2
A Operand STACK esta vazia agora, x = 0; porem deveria ser 5, mais eu sobrescrevi o valor. (E não era essa a intenção)
Conseguiu enxergar a inconsistência de valores? eu simplesmente tinha uma instance variable, com o valor de 5
public int x = 5;
Oque era para acontecer:
Em uma thread eu gostaria de somar + 5...
x = x + 5,
x = 5 + 5;
x = 10;-> Thread A
Em uma outra thread eu gostaria de subtrair 5...
x = x - 5;
x = 10 - 5;
x = 5; - Thread B
Oque aconteceu: x = 0;
No final meu x foi para 0(e era para ser 5), houve uma inconsistência de valores, devido ao fato que temos 2 stacks acessando o mesmo OBJETO supondo que ocorra timeSlicing oque é muito provável e obviou que elas tem a mesma prioridade.
Foi como dito antes, certas coisas são como átomos, não podemos dividir.
Como consertar isso?
Bom sabemos que Garantia de haver ou não Robin-Round(claro e o timeSlicing)não podemos ter, Portanto fazemos o seguinte:
TODO objeto em java tem um cadeado, ESSE CADEADO É DO OBJETO logo esse cadeado pode estar trancado ou destrancado. Só que esse mecanismo só fica ativo quando usamos a keyword synchronized.
Quando um cadeado esta trancado?
Quando uma thread acessa um membo synchronized, ou uma instrução synchronized, A thread se tranca em um "quarto" com o objeto, e diz que só devolve a chave do quarto, QUANDO ELA TERMINAR O SERVIÇO.
E se uma threadB quer usar o Objeto, porem a threadA esta com a chave?
A thread B fica em um Pool CHAMADO: Blocked in Object’s Lock Pool. E so sai de la quando a thread A, sair do quarto e devolver a chave.
SIMPLESMENTE synchronized evita o Robin-Round(claro e o timeSlicing) referente aquele objeto, evita usando o mecanismo de tranca onde a thread fica com a chave,outras threads ate podem acessar o método, so que ficam no Object’s Lock Pool esperando a outra thread sair do "quarto" e devolver a chave.
EXEMPLO DE Método de somar e subtrair synchronized:
Object = é um objeto com uma instance variable (public int x = 5) e 2 métodos sicronizados(somar(), subtrair())
Thread A = é a Thread de SOMA
Thread B = é a thread de subtração
*Thread A entra invoca somar() do objeto Object
getfield #2
iconst_5
iadd <----
putfield #2
Na instrução iconst_5, terminou o time da thread A, portanto time da Thread B
*Thread B tentar entra invocar subtrair do objeto Object, POREM Não pode, pois não tem a chave para destrancar(Pois subtrair é SINCRONIZADO). PORTANTO THREAD B entra no POOL das Blocked in Object’s Lock Pool.
*Thread A volta da onde tinha parado, e continua o somar() do Object
getfield #2
iconst_5
iadd
putfield #2
agora sim, x = x + 5...x = 10; Thread A terminou o bloco de código do frame , abriu "o quarto" e devolveu a chave.
*Thread B tentar entra invocar subtrair do objeto Object, COMO thread A terminou la esta a chave, B PEGA A CHAVE, se tranca com o objeto "no quarto"e manda a ver no metodo!
getfield #2
iconst_5
isub
putfield #2
AGORA TEMOS O RESULTADO QUE QUERIAMOS, x = 5
TENHO que synchronized o método todo?
Não você pode simplesmente synchronized o código que você quer, especificando um objeto que tem a chave para aquilo.(Eles te dão a chance de ter um bloco onde se consegue garantir o termino coerentemente e onde você pode especificar outro objeto para a obtenção da tranca disso)
public void go(){
synchronized(this){
}
}
Ou declare uma instance variables somente para armazenar a Tranca dele
Object d = new Object();
synchronized(d){
}
Resumo:
- Quando duas threads acessam o mesmo objeto tenha CUIDADO, potenciais problemas podem vir a ocorrer.
- TODO OBJETO TEM UM CADEADO ASSOCIADO COM ELE.
- O cadeado e a chave dele, estão associados ao objeto … A thread simplesmente tranca ele e pega a chave, porem devolve ao OBJETO DEPOIS. Portanto 2 métodos synchronized significa só uma chave para acessar qualquer um dos dois.
- Lembre-se só sincroniza - se membros, e construtor não é considerado membro
- Quando uma thread acessa um membro synchronized do objeto, outras threads não poderão acessar SOMENTE MEMBROS synchronized DESSE OBJETO, ATE QUE A PRIMEIRA THREAD DESTRANQUE...REPITO SOMENTE MEMBROS synchronized QUE ELES NÃO PODERÃO ACESSAR.
- Thread safe é estar synchronized.
- O grande problema de não fazer membros thread safe não é a questão de timeslicing mais sim das intruções que já podem ter sido feitas pelo pc do pc Register.
- Podemos fazer métodos (thread safe) ou code_blocks (thread safe), porem em questão de code_blocks é necessário especificar o objeto.
- Se for um membro static, a thread tranca os membros synchronized da classe, e só devolve a chave para a classe quando terminar.
- Quando uma thread A acessa o método/instrução sincronizada de um objeto, e thread B tenta acessar o um método/instrução sincronizada do mesmo objeto, SE thread A ainda não terminou, thread B vai para o estado “Bloqueado em pool de Bloqueio de Objeto” e so sai de la quando a devolver a chave do objeto.
Passo a passo:
1 - thread A entra em um método/instrução sincronizada do objeto1
2 - thread B tenta entrar em algum método/instrução sincronizada do objeto1(mesmo de thread A)
3 -thread B encontra um objeto1 fechado para métodos/instruções sincronizadas e não encontra a chave, thread B vai para o estado: “Bloqueado em pool de bloqueio de objeto”
4 - thread A termina o code_block(ou vai dormir em uma instrução não sincronizada) e libera a chave do objeto1
5 - thread B agora vai para o estado Executável e como só temos essa thread de maior prioridade o escalonador seleciona thread B para ser RUNNING.
Fácil sincronização neh?
CYA DUDES!!!
Nenhum comentário:
Postar um comentário