Recentemente, encontrei alguns cenários de alta concorrência no trabalho que exigem o bloqueio para garantir a correção da lógica dos negócios e o desempenho após o bloqueio não deve ser afetado demais. A idéia inicial é bloquear os dados por meio de palavras -chave, como registros de data e hora e IDs, garantindo assim a simultaneidade de diferentes tipos de processamento de dados. A granularidade da trava fornecida pela própria API de Java é muito grande e é difícil atender a essas necessidades ao mesmo tempo, então escrevi várias extensões simples ...
1. Bloqueio do segmento
Com base na idéia de segmentação de concorrente, é criado um certo número de bloqueios e, ao usá -lo, o bloqueio correspondente é retornado de acordo com a chave. Esse é o desempenho mais simples e mais alto e, finalmente, adotou a estratégia de bloqueio entre várias implementações. O código é o seguinte:
/*** Bloqueio do segmento, o sistema fornece um certo número de bloqueios originais, obtenha o bloqueio correspondente com base no valor de hash do objeto de entrada e adicione o bloqueio* Nota: Se o valor de hash do objeto a ser bloqueado, pode fazer com que o bloqueio não puder ser liberado com sucesso !!! */public class SegmentLock <T> {segmentos inteiros privados = 16; // Número padrão de segmentos HashMap final privado <Inteiro, Reentrantlock> LockMap = new Hashmap <> (); public segmentLock () {init (nulo, false); } public segmentLock (contagens de número inteiro, feira booleana) {init (contagens, justo); } private void init (contagem de número inteiro, feira booleana) {if (contagens! = null) {segmentos = contagens; } para (int i = 0; i <segmentos; i ++) {lockmap.put (i, novo reentrantlock (justo)); }} public void bloqueio (tecla t) {reentrantlock Lock = LockMap.get ((key.hashcode () >>> 1) % segmentos); Lock.lock (); } public void desbloqueio (tecla t) {reentrantlock bloqueio = lockmap.get ((key.hashcode () >>> 1) segmentos %); Lock.Unlock (); }}2. Lock de hash
A segunda estratégia de bloqueio desenvolvida com base no bloqueio segmentado acima é obter uma verdadeira trava de grão fino. Cada objeto com um valor de hash diferente pode obter seu próprio bloqueio independente. No teste, quando o código bloqueado é executado rapidamente, a eficiência é cerca de 30% mais lenta que o bloqueio do segmento. Se houver uma operação de longa data, a sensação de desempenho deve ser melhor. O código é o seguinte:
classe pública hashlock <t> {private boolean isfair = false; Segmentlock final privado <t> segmentLock = new segmentLock <> (); // Lock de segmento privado final ConcurrentHashMap <t, LockInfo> LockMap = new ConcurrentHashMap <> (); public hashlock () {} public hashlock (Boolean Fair) {isfair = Fair; } public void Lock (tecla T) {Lockinfo LockInfo; segmentlock.lock (chave); tente {LockInfo = LockMap.get (chave); if (LockInfo == NULL) {LockInfo = new LockInfo (isfair); LockMap.put (tecla, LockInfo); } else {LockInfo.Count.IncregandGet (); }} finalmente {segmentlock.unlock (chave); } lockinfo.lock.lock (); } public void desbloqueio (tecla t) {LockInfo LockInfo = LockMap.get (chave); if (LockInfo.Count.get () == 1) {segmentlock.lock (chave); tente {if (lockinfo.count.get () == 1) {LockMap.remove (chave); }} finalmente {segmentlock.unlock (chave); }} Lockinfo.Count.DecRementAndget (); Lockinfo.unlock (); } classe estática privada Lockinfo {public reentrantlock Lock; contagem pública atômica = novo atomicinteger (1); Private Lockinfo (Boolean Fair) {this.lock = new Reentrantlock (Fair); } public void Lock () {this.lock.lock (); } public void desblock () {this.lock.unlock (); }}}3. Lock de referência fraco
Os bloqueios de hash são sempre falhos porque os bloqueios segmentados são introduzidos para garantir a sincronização da criação e destruição de bloqueio, portanto uma terceira trava foi gravada para buscar um melhor desempenho e bloqueios de grão mais fino. A idéia desse bloqueio é criar um bloqueio com a ajuda de referências fracas em Java e entregar a destruição da fechadura à coleção de lixo da JVM para evitar o consumo adicional.
É um pouco lamentável que, como o concorrente é usado como contêiner de trava, é incapaz de se livrar de bloqueios segmentados. O desempenho desse bloqueio é cerca de 10% mais rápido que o de hashlock. Código de bloqueio:
/*** Bloqueio de referência fraco, fornecendo função de bloqueio independente para cada hash independente*/public classe fracashashlock <t> {private ConcurrentHashmap <t, fracarlockref <t, reentrantlock >> LockMap = new ConcurrentHashmap <> (); Reference PrivateQueue <Reentrantlock> fila = new ReferenceQueue <> (); public reentrantlock get (t chave) {if (lockmap.size ()> 1000) {clearEmptyref (); } FrAcheReference <ReentrantLock> LockRef = LockMap.get (chave); Reentrantlock bloqueio = (LockRef == null? NULL: LockRef.get ()); while (Lock == NULL) {LockMap.putifabsent (chave, new fratLockRef <> (new reentrantlock (), fila, chave)); LockRef = LockMap.get (chave); Lock = (LockRef == NULL? NULL: LockRef.get ()); if (Lock! = NULL) {return Lock; } ClearEmptyref (); } retornar bloqueio; } @Suppresswarnings ("desmarcado") private void clearEmptyref () {reference <? estende o Reentrantlock> Ref; while ((ref = fileue.poll ())! = null) {fracarlockref <t ,? estende o reentrantlock> fracarlockRef = (fracarlockref <t ,? estende reentrantlock>) ref; LockMap.Remove (fracoslockref.key); }} classe final estática privada fracoslockref <t, k> estende a fraca referência <k> {final T Key; privado fracarlockref (k referência, referencequeue <? super k> q, t chave) {super (referente, q); this.Key = key; }}}PostScript
No começo, eu queria usar o LockSupport e o AQS para implementar bloqueios de grão fino. Como escrevi, descobri que as coisas que eu estava implementando não eram muito diferentes daquelas travas nativas de Java, então desisti do encapsulamento dos próprios bloqueios de Java, que perdiam muito tempo.
De fato, após a implementação desses bloqueios de refrigeração fina, temos novas idéias, como enviar dados para tópicos especiais por meio de idéias de segmentação, que podem reduzir o tempo de bloqueio de um grande número de threads e deixá-lo para a exploração futura ...