Essa eh a alterativa a usar synchronized do Tiger,alguem jah testou/usou para dizer a performance desse ReeentrantLock?
Leiam aqui:
http://www-106.ibm.com/developerworks/library/j-jtp10264/
Não cheguei a precisar, e também não iria fazer um teste contra o sync. mas parece ser muito interessante.
Faltou no artigo explicar melhor isso:
:arrow: timed lock waits
Não entendi, isso não é o wait(long timeout) ???
:arrow: interruptible lock waits
Não entendi, o wait não é interrompível ??? (thows InterruptedException)
:arrow: non-block-structured locks
Não entedi essa parte !!! Alguém entendeu ???
:arrow: lock polling
Isso deve ser para aumentar a performance, não li essa parte do artigo, logo tb não entendi muito bem. :?
:arrow: multiple condition variables
Isso sim me pareceu bem legal, já que simplifica as coisas e traz mais clareza ao código.
Para entender isso eu tentei reescrever o código abaixo da maneira antiga, ou seja, com synchronized e wait/notify.
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
(Não testei nem compilei isso…)
class BoundedBuffer {
final Object putLock = new Object();
final Object takeLock = new Object();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
synchronized(putLock) {
// deadlock !!!!
synchronized(takeLock) {
while (count == items.length) takeLock.wait();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
putLock.notify();
}
}
}
public Object take() throws InterruptedException {
synchronized(takeLock) {
// deadlock !!!
synchronized(putLock) {
while (count == 0) putLock.wait();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
takeLock.notify();
return x;
}
}
}
}
Como falei no meu tutorial, toda vez que vc tem um lock dentro do outro uma luz vermelha deve acender indicando: DEADLOCK !!!
Como resolver esse deadlock ??? Não sei !!! Alguém sabe ??? O único jeito é voltar para um lock só:
(Não testei nem compilei isso…)
public class BoundedBuffer {
final Object[] items = new Object[100];
int putptr, takeptr, count;
public synchronized void put(Object x) throws InterruptedException {
while (count == items.length) wait();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notifyAll();
}
public synchronized Object take() throws InterruptedException {
while (count == 0) wait();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notifyAll();
return x;
}
}
Moral da história, com o ReentrantLock podemos separar em dois grupos a galera que está esperando um take e a galera que está esperando um put, e ir acordando um a um bonitinho.
Do método antigo, a galera ter que ficar num grupo só.
Só que agora deu um deadlock na minha cabeça. :crazyeyes:
Não é impossível ter dois grupos esperando ao mesmo tempo ???
Se eu tenho uma fila no take obrigatoriamente a fila do put está vazia e se eu tenho uma fila no put a fila do take obrigatoriamente está vazia.
Não faz sentido ter gente esperando para put e take ao mesmo tempo !!!
Qual a vantagem disso então ??? Acho que eu perdi algum ponto importante da história…
Da uma lida na api da classe lock, gostei da explicação de la.
http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/locks/Lock.html
[quote=“saoj”]
Como falei no meu tutorial, toda vez que vc tem um lock dentro do outro uma luz vermelha deve acender indicando: DEADLOCK !!!
Como resolver esse deadlock ??? Não sei !!! Alguém sabe ??? O único jeito é voltar para um lock só:[/quote]
Se voce pegar os locks sempre na mesma ordem nunca da deadlock.
Sergio, você está confundindo as coisas.
Em java, todos objetos são monitors, ou seja, lock + condition variable. Não se deve misturar essas duas primitivas na hora de escrever código MT.
Logo as considerações do autor são válidas. Com ReentrantLock a coisa é assim:
-aquisição com timeout, synchronized não permite isso.
-possibilidade de se interromper uma thread esperando pelo lock, synchronized não permite isso.
-usando synchronized seu código fica restrito ao escopo que ele foi usado. Com RL não tem dessa:
public void a() {
ReentrantLock rl = new ReentrantLock();
rl.lock();
try {
b(rl);
c(rl);
} finally {
if(nãoFezNada)
rl.unlock();
}
}
public void b(ReentrantLock l) {
if(condicaoB) l.unlock();
else fazMonteDeCoisas();
}
public void c(ReentrantLock l) {
if(condicaoC) l.unlock();
else fazOutroMonteDeCoisas();
}
Isso permite criar código com muito menos contenção.
Outra coisa é que permite criar código mais seguro contra deadlocks usando protocolos de sincronização. Algo assim:
public interface ConcurrentCommand {
List<? extends Lock> getRequiredLocks();
void execute();
}
public void executeBatch(List<? extends ConcurrentCommand> cmds) {
List<Lock> locks = new ArrayList<Lock>();
for(i : cmds) locks.addAll(i.getRequiredLocks());
Collections.sort(locks, new LocksComparator());
for(c : locks) c.lock(); //não é tão simples assim, precisa tratar interrupção
try {
for(cc : cmds) cc.execute();
} finally {
for(c: locks) c.unlock();
}
}
Isso é impossivel de ser feito usando simplesmente sinchronized. A única forma é implementando algo semelhante a ReentrantLock.
-Pooling de locks é útil, nada de especial nisso.
-Uso de múltiplas variaveis de condição. Com o mecanismo de wait/notify, você precisa de um monitor, logo 1 lock, por condição. Isso pode acabar sendo um saco se precisar usar várias condições. Com Reentrant lock as coisas ficam bem mais simples.
A moral da historia, se existe uma, é não use java.util.concurrent se não estiver escrevendo código MT muito avançado e você não for um programador muito experiênte com isso.
[quote=“louds”]Sergio, você está confundindo as coisas.
Em java, todos objetos são monitors, ou seja, lock + condition variable. Não se deve misturar essas duas primitivas na hora de escrever código MT.
[/quote]
Isso eu entendo perfeitamente !!! :?
Não sei se vc percebeu que com o ReentrantLock um lock pode ter N condiçoes agora e não apenas uma como antigamente.
Vc viu o exemplo do BoundedBuffer, que usa um lock + duas condiçoes ???
Minha duvida foi, porque no BoundedBuffer é legal ter duas condiçoes se na minha cabeca (posso estar enganado) sempre que houver gente esperando na fila do take a fila do put estará vazia e vice-versa ???
Usando uma condição apenas, isto é, usando a forma antiga de um lock + uma condição (Object) não dá no mesmo no final das contas ??? Alguma coisa eu não estou captando nessa jogada. Que todo objeto em Java é lock e monitor ao mesmo tempo isso eu já captei a algum tempo.