|
| 1 | +:java-package: src/org/j6toj8/concurrency |
| 2 | +:section-java-package: ../../../{java-package} |
| 3 | + |
| 4 | +=== Locks |
| 5 | + |
| 6 | +.Objetivo |
| 7 | +-------------------------------------------------- |
| 8 | +Use Lock, ReadWriteLock, and ReentrantLock classes in the java.util.concurrent.locks and java.util.concurrent.atomic packages to support lock-free thread-safe programming on single variables |
| 9 | +- |
| 10 | +Usar classes dos tipos Lock, ReadWriteLock, e ReentrantLock dos pacotes java.util.concurrent.locks e java.util.concurrent.atomic para suportar programação sem bloqueio e multi-thread em variáveis únicas |
| 11 | +-------------------------------------------------- |
| 12 | + |
| 13 | +As classes e interfaces `Lock`, `ReadWriteLock` e `ReentrantLock` permitem obter diferentes tipos de _Locks_ (em tradução livre: bloqueios ou travas). Esses _Locks_ são utilizados para que um número limitado de _threads_ tenham acesso a mesma variável em um determinado momento, ou para que apenas uma delas possa alterar seu valor. |
| 14 | + |
| 15 | +Nesta seção serão apresentados exemplos utilizando essas classes e interfaces. Assim como em outras seções deste capítulo, os exemplos podem ser grandes quando for necessário a criação de __threads__. Dedique um tempo maior para entendê-los completamente. |
| 16 | + |
| 17 | +==== Reentrant Lock |
| 18 | + |
| 19 | +. É possível adquirir um _Lock_ utilizando a classe ``ReentrantLock``. |
| 20 | ++ |
| 21 | +[source,java,indent=0] |
| 22 | +.{java-package}/locks/Locks_ReentrantLock.java |
| 23 | +---- |
| 24 | +include::{section-java-package}/locks/Locks_ReentrantLock.java[tag=code] |
| 25 | +---- |
| 26 | ++ |
| 27 | +.Saída no console |
| 28 | +[source,console] |
| 29 | +---- |
| 30 | +ABC |
| 31 | +---- |
| 32 | ++ |
| 33 | +Perceba que o _lock_ é removido dentro de um bloco ``finally``. Isso garante que uma _thread_ não irá ficar com um _lock_ indeterminadamente. |
| 34 | + |
| 35 | +. Chamar o método unlock sem ter obtido um lock anteriormente irá lançar uma exceção. |
| 36 | ++ |
| 37 | +[source,java,indent=0] |
| 38 | +.{java-package}/locks/Locks_UnlockWithoutLock.java |
| 39 | +---- |
| 40 | +include::{section-java-package}/locks/Locks_UnlockWithoutLock.java[tag=code] |
| 41 | +---- |
| 42 | ++ |
| 43 | +.Saída no console |
| 44 | +[source,console] |
| 45 | +---- |
| 46 | +ABC |
| 47 | +Exception in thread "main" java.lang.IllegalMonitorStateException |
| 48 | + at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151) |
| 49 | + at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261) |
| 50 | + at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457) |
| 51 | + at org.j6toj8.concurrency.locks.Locks_UnlockWithoutLock.main(Locks_UnlockWithoutLock.java:14) |
| 52 | +---- |
| 53 | + |
| 54 | +. É possível tentar obter um _lock_ imediatamente utilizando o método ``tryLock``. |
| 55 | ++ |
| 56 | +[source,java,indent=0] |
| 57 | +.{java-package}/locks/Locks_TryLock.java |
| 58 | +---- |
| 59 | +include::{section-java-package}/locks/Locks_TryLock.java[tag=code] |
| 60 | +---- |
| 61 | ++ |
| 62 | +.Saída no console |
| 63 | +[source,console] |
| 64 | +---- |
| 65 | +ABC |
| 66 | +---- |
| 67 | + |
| 68 | +. Também é possível tentar obter um _lock_ definindo um tempo de espera máximo. |
| 69 | ++ |
| 70 | +[source,java,indent=0] |
| 71 | +.{java-package}/locks/Locks_TryLockTimeout.java |
| 72 | +---- |
| 73 | +include::{section-java-package}/locks/Locks_TryLockTimeout.java[tag=code] |
| 74 | +---- |
| 75 | ++ |
| 76 | +.Saída no console |
| 77 | +[source,console] |
| 78 | +---- |
| 79 | +ABC |
| 80 | +---- |
| 81 | + |
| 82 | +. Em um cenário com várias __threads__, é possível que apenas uma delas consiga obter um __lock__. |
| 83 | ++ |
| 84 | +[source,java,indent=0] |
| 85 | +.{java-package}/locks/Locks_TryLockMultithread.java |
| 86 | +---- |
| 87 | +include::{section-java-package}/locks/Locks_TryLockMultithread.java[tag=code] |
| 88 | +---- |
| 89 | ++ |
| 90 | +.Saída no console |
| 91 | +[source,console] |
| 92 | +---- |
| 93 | +Thread-0: Conseguiu o Lock |
| 94 | +Thread-2: Conseguiu o Lock |
| 95 | +---- |
| 96 | ++ |
| 97 | +Nesta execução com 3 __threads__, apenas duas conseguiram obter o _lock_ imediatamente e imprimir no console. Porém o resultado é imprevisível. Podem existir execuções onde todas obterão o __lock__, e outras em que apenas uma thread conseguirá. |
| 98 | + |
| 99 | +. Uma _thread_ pode obter mais de um _lock_ no mesmo objeto ``Lock``, mas deve desfazer o _lock_ múltiplas vezes também. |
| 100 | ++ |
| 101 | +[source,java,indent=0] |
| 102 | +.{java-package}/locks/Locks_LockTwice.java |
| 103 | +---- |
| 104 | +include::{section-java-package}/locks/Locks_LockTwice.java[tag=code] |
| 105 | +---- |
| 106 | ++ |
| 107 | +.Saída no console |
| 108 | +[source,console] |
| 109 | +---- |
| 110 | +ABC |
| 111 | +---- |
| 112 | ++ |
| 113 | +Como a _thread_ chamou `lock` duas vezes, caso ela não houvesse chamado `unlock` duas vezes, outra _thread_ não seria capaz de obter o __lock__. |
| 114 | + |
| 115 | +. É possível garantir uma distribuição mais "justa" de _locks_ passando `true` como argumento para o ``ReentrantLock``. |
| 116 | ++ |
| 117 | +[source,java,indent=0] |
| 118 | +.{java-package}/locks/Locks_Fair.java |
| 119 | +---- |
| 120 | +include::{section-java-package}/locks/Locks_Fair.java[tag=code] |
| 121 | +---- |
| 122 | ++ |
| 123 | +Ao passar o argumento ``true``, quando várias _threads_ estiverem esperando pelo mesmo __lock__, ele será dado àquela _thread_ que está aguardando a mais tempo. |
| 124 | + |
| 125 | +==== ReentrantReadWriteLock |
| 126 | + |
| 127 | +. É possível separar _locks_ de leitura e escrita utilizando a classe ``ReadWriteLock``. _Locks_ de leitura podem ser obtidos por múltiplas _threads_, porém _locks_ de escrita não. |
| 128 | ++ |
| 129 | +[source,java,indent=0] |
| 130 | +.{java-package}/locks/Locks_ReadWriteLock.java |
| 131 | +---- |
| 132 | +include::{section-java-package}/locks/Locks_ReadWriteLock.java[tag=code] |
| 133 | +---- |
| 134 | ++ |
| 135 | +.Saída no console |
| 136 | +[source,console] |
| 137 | +---- |
| 138 | +Thread-0: Conseguiu o Lock de leitura |
| 139 | +Thread-2: Conseguiu o Lock de leitura |
| 140 | +Thread-1: Conseguiu o Lock de leitura |
| 141 | +Thread-1: Conseguiu o Lock de escrita |
| 142 | +---- |
| 143 | ++ |
| 144 | +Perceba que todas as _threads_ conseguiram obter o _lock_ de leitura, porém apenas uma conseguiu obter o _lock_ de escrita. |
| 145 | + |
| 146 | +. Se uma _thread_ já possuir o _lock_ de escrita, outras não conseguirão obter nem mesmo o _lock_ de leitura. |
| 147 | ++ |
| 148 | +[source,java,indent=0] |
| 149 | +.{java-package}/locks/Locks_ReadWriteLockInverted.java |
| 150 | +---- |
| 151 | +include::{section-java-package}/locks/Locks_ReadWriteLockInverted.java[tag=code] |
| 152 | +---- |
| 153 | ++ |
| 154 | +.Saída no console |
| 155 | +[source,console] |
| 156 | +---- |
| 157 | +Thread-0: Conseguiu o Lock de escrita |
| 158 | +Thread-0: Conseguiu o Lock de leitura |
| 159 | +---- |
| 160 | ++ |
| 161 | +Perceba que neste exemplo o _lock_ de escrita está sendo obtido *antes* do de leitura, de tal forma que apenas a primeira _thread_ que foi executada conseguiu obter os dois __locks__. |
| 162 | + |
| 163 | +**** |
| 164 | +
|
| 165 | +* Applying Locks |
| 166 | ++ |
| 167 | +Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer II Study Guide (p. 607). Wiley. Edição do Kindle. |
| 168 | +
|
| 169 | +* https://www.baeldung.com/java-concurrent-locks[Guide to java.util.concurrent.Locks.] |
| 170 | +
|
| 171 | +* https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/package-summary.html[Package java.util.concurrent.locks.] Java Plataform SE 8. |
| 172 | +
|
| 173 | +**** |
0 commit comments