terça-feira, 25 de agosto de 2009

Sincronização:

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