0. SOBRE MUTEX
A chamada bloqueio mutex refere-se a uma fechadura que só pode ter um thread de cada vez. Antes do JDK1.5, geralmente usamos o mecanismo sincronizado para controlar o acesso de vários threads a recursos compartilhados. Agora, o Lock fornece uma gama mais ampla de operações de bloqueio do que o mecanismo sincronizado. As principais diferenças entre mecanismos de trava e sincronizado:
O mecanismo sincronizado fornece acesso a bloqueios de monitor implícitos associados a cada objeto e força toda a aquisição e liberação de bloqueio a aparecer em uma estrutura de bloco. Quando vários bloqueios são adquiridos, eles devem ser liberados em ordem inversa. O mecanismo sincronizado libera os bloqueios implicitamente. Enquanto o código executado pelo encadeamento exceder o escopo do bloco de instrução sincronizado, o bloqueio será liberado. O mecanismo de bloqueio deve chamar explicitamente o método desbloqueio () do objeto de bloqueio para liberar o bloqueio, que fornece a possibilidade de aquisição e liberação dos bloqueios para não aparecer na mesma estrutura de blocos e liberar os bloqueios em uma ordem mais livre.
1. Introdução ao Reentrantlock
O ReentrantLock é um bloqueio mutex reentrante, também conhecido como "bloqueio exclusivo".
Como o nome sugere, uma trava reentrantlock só pode ser mantida por um bloqueio de rosca no mesmo momento; Enquanto o reentrante significa que um bloqueio de reentrantlock pode ser adquirido várias vezes por um único thread.
O Reentrantlock é dividido em "Bak Lock" e "Lock Deslear". Suas diferenças se refletem sobre se o mecanismo de obtenção de bloqueios é justo. "Lock" é proteger os recursos concorrentes e impedir que vários threads operem threads ao mesmo tempo e erros. O ReentrantLock só pode ser adquirido por um thread ao mesmo tempo (quando um thread adquire o "bloqueio", outros threads devem esperar); A reentrada gerencia todos os tópicos que adquirem a fechadura através de uma fila de espera do FIFO. Sob o mecanismo de "Fair Lock", encadeamentos na fila para adquirir o bloqueio em sequência; Enquanto "Lock Non-Fair" adquirirá a fechadura, independentemente de estar no início da fila ou não.
Lista de funções reentrantlock
// Crie um reentrantlock, que é "trava injusta" por padrão. Reentrantlock () // A política de criação é o reentrantlock da feira. Se justo for verdadeiro, significa que é um bloqueio justo e, se justo é falso, significa que é um bloqueio não fair. Reentrantlock (Boolean Fair) // Consulta o número de vezes que o encadeamento atual mantém esse bloqueio. int GetholdCount () // Retorna o thread que atualmente possui esse bloqueio e, se esse bloqueio não pertence a nenhum thread, retorne nulo. Thread Protected Getwner () // Retorna uma coleção que contém o thread que pode estar esperando para adquirir esse bloqueio. Coleção protegida <Thread> getQueuedthreads () // Retorna o número estimado de threads aguardando para adquirir esse bloqueio. int getQueuelngth () // retorna uma coleção que contém os threads que podem estar esperando por uma determinada condição relacionada a esse bloqueio. Coleção protegida <Thread> getWaitingThreads (condição de condição) // Retorna a estimativa do encadeamento aguardando a condição especificada associada a esse bloqueio. int getWaitQueuel comprimento (condição) // Consulta se o thread fornecido está esperando para adquirir esse bloqueio. Boolean HasqueedThread (Thread) // Consulta se alguns threads estão esperando para adquirir esse bloqueio. Boolean HasqueedThreads () // Consulta se alguns threads estão aguardando uma determinada condição relacionada a esse bloqueio. Boolean Haswaiters (condição da condição) // Retorne true se for "Fair Lock", caso contrário, retorne falsos. boolean isfair () // consulta se o encadeamento atual mantém esse bloqueio. boolean isheldbycurrentThread () // Query se esse bloqueio é mantido por qualquer thread. booleano islocked () // pegue a fechadura. Void Lock () // Se o encadeamento atual não for interrompido, o bloqueio será adquirido. Void LockInterruptível () // Retorna a instância de condição usada para usar com esta instância de bloqueio. Condição newcondition () // só adquire a trava se não for mantida por outro thread durante a chamada. Boolean Trylock () // Se o bloqueio não for mantido por outro thread dentro de um determinado tempo de espera e o thread atual não será interrompido, o bloqueio será adquirido. BOOLEAN TRYLOCK (longo tempo limite, unidade de unidade de tempo) // Tente liberar esse bloqueio. desbloqueio void ()
2. Exemplo de reentrantlock
Ao comparar "Exemplo 1" e "Exemplo 2", podemos entender claramente o papel de travar e desbloquear
2.1 Exemplo 1
importar java.util.concurrent.locks.lock; importar java.util.concurrent.locks.reentrantlock; // LockTest1.java// Classe de repositório depósito {private int tamanho; // o número real de bloqueio privado do repositório; // exclusivo Lock public depot () {this.size = 0; this.lock = new reentrantlock (); } public void Produce (int val) {Lock.lock (); tente {size += val; System.out.printf ("%s Produce (%d) -> size =%d/n", Thread.currentThread (). GetName (), val, tamanho); } finalmente {Lock.unlock (); }} consumo público vazio (int val) {Lock.lock (); tente {size -= val; System.out.printf ("%s consumo (%d) <- size =%d/n", thread.currentThread (). GetName (), val, size); } finalmente {Lock.unlock (); }}}; // Classe Produtor Produtor {Depósito de Depósito Privado; produtor público (depósito depósito) {this.depot = deposit; } // Produtos de consumo: Crie um novo thread para produzir produtos no armazém. public void Produce (final int val) {new Thread () {public void run () {deposit.produce (val); } }.começar(); }} // Classe de consumidor CLIENTE {private depósito depósito; cliente público (depósito depósito) {this.depot = depósito; } // Produto do consumidor: Crie um novo thread para consumir o produto no armazém. public void Consumo (final int val) {new Thread () {public void run () {depot.consume (val); } }.começar(); }} classe pública Locktest1 {public static void main (string [] args) {depot mdepot = new Depot (); Produtor MPro = Novo Produtor (MDepot); Cliente mcus = novo cliente (mdepot); mpro.produce (60); mpro.produce (120); mcus.consume (90); mcus.consume (150); mpro.produce (110); }} Resultados em execução:
Thread-0 Produce (60)-> Tamanho = 60ThRead-1 Produce (120)-> Tamanho = 180Thread-3 Consume (150) <-Tamanho = 30Thread-2 Consuma (90) <-Tamanho = -60Thread-4 Produce (110)-> Tamanho = 50
Análise de resultados:
(1) O depósito é um armazém. As mercadorias podem ser produzidas no armazém através de produtos (), e as mercadorias no armazém podem ser consumidas através do consumo (). O acesso mutuamente exclusivo ao armazém é alcançado através da trava exclusiva: Antes de operar as mercadorias no armazém (produção/consumo), o armazém será bloqueado pelo bloqueio () primeiro e depois desbloqueado através do desbloqueio () após a conclusão da operação.
(2) Produtor é um produtor. Chamar a função Produzir () no produtor pode criar um novo thread para produzir produtos no armazém.
(3) O cliente é uma categoria de consumidor. Chamar a função de consumo () no cliente pode criar um novo produto de consumo de threads no armazém.
(4) No principal tópico principal, criaremos um novo produtor MPro e um novo MCUS de consumidores. Eles produzem/consumem produtos em armazéns, respectivamente.
De acordo com a quantidade de produção/consumo em Main, o produto restante final no armazém deve ser 50. Os resultados da operação estão alinhados com nossas expectativas!
Existem dois problemas com este modelo:
(1) Na realidade, a capacidade do armazém não pode ser negativa. No entanto, a capacidade do armazém nesse modelo pode ser negativa, o que contradiz a realidade!
(2) Na realidade, a capacidade do armazém é limitada. No entanto, realmente não há limite para a capacidade neste modelo!
Falaremos brevemente sobre como resolver esses dois problemas. Agora, vamos dar uma olhada em um exemplo simples 2 primeiro; Ao comparar "Exemplo 1" e "Exemplo 2", podemos entender o objetivo de Lock () e desbloquear () mais claramente.
2.2 Exemplo 2
importar java.util.concurrent.locks.lock; importar java.util.concurrent.locks.reentrantlock; // Locktest2.java// Classe Repository Depot {private int tamanho; // o número real de bloqueio privado do repositório; // exclusivo Lock public depot () {this.size = 0; this.lock = new reentrantlock (); } public void Produce (int val) {// Lock.lock (); // tente {size += val; System.out.printf ("%s produz (%d) -> size =%d/n", thread.currentThread (). GetName (), val, size); //} catch (interruptedException e) {//} finalmente {//lock.unlock (); //}}}}; System.out.printf ("%s consume (%d) <- size =%d/n", thread.currentThread (). GetName (), val, size); //} finalmente {// lock.unlock (); //}}}; // produtor produtor {private depos Depos Depos Depos; Produtor público (depósito depósito) {this.depot = depósito; } // Produto do consumidor: Crie um novo thread para produzir o produto no armazém. public void Produce (final int val) {new Thread () {public void run () {deposit.produce (val); } }.começar(); }} // Classe de consumidor CLIENTE {private depósito depósito; cliente público (depósito depósito) {this.depot = depósito; } // Produto do consumidor: Crie um novo thread para consumir o produto no armazém. public void Consumo (final int val) {new Thread () {public void run () {depot.consume (val); } }.começar(); }} classe pública Locktest2 {public static void main (string [] args) {depot mdepot = new Depot (); Produtor MPro = Novo Produtor (MDepot); Cliente mcus = novo cliente (mdepot); mpro.produce (60); mpro.produce (120); mcus.consume (90); mcus.consume (150); mpro.produce (110); }} (Uma vez) resultado:
Thread-0 Produce (60)-> Tamanho = -60Thread-4 Produce (110)-> Size = 50Thread-2 Consumo (90) <-Size = -60Thread-1 Produce (120)-> Tamanho = -60Thread-3 Consumo (150) <-Tamanho = -60
Resultados Descrição:
"Exemplo 2" remove a trava com base no "Exemplo 1". No Exemplo 2, o produto restante final no armazém é -60, não os 50 que esperávamos. O motivo é que não implementamos o acesso mutex ao repositório.
2.3 Exemplo 3
Em "Exemplo 3", usamos a condição para resolver dois problemas em "Exemplo 1": "A capacidade do armazém não pode ser negativa" e "a capacidade do armazém é limitada".
A solução para esse problema é através da condição. A condição precisa ser usada em conjunto com o bloqueio: o método Await () na condição pode fazer com que o encadeamento bloqueie [semelhante ao wait ()]; O método de condição Signal () pode causar o encadeamento de despertar [semelhante a notificar ()].
importar java.util.concurrent.locks.lock; importar java.util.concurrent.locks.reentrantlock; importar java.util.concurrent.locks.condition; // locktest3.java// warehouse classe depot {private int capacidade; // Capacidade de armazém Tamanho privado int; // o número real de trava privada de armazém; // Condição privada de bloqueio exclusiva Condição completa; // Condições de produção em esvaziamento de condição privada; // Condições de consumo Public Depot (int Capacidade) {this.Capacity = Capacidade; this.size = 0; this.lock = new reentrantlock (); this.fullCondtion = Lock.NewCondition (); this.emptyCondition = Lock.NewCondition (); } public void Produce (int val) {Lock.lock (); tente {// esquerda significa "a quantidade que você deseja produzir" (pode ser muita produção, então você precisa produzir mais) int left = val; enquanto (esquerda> 0) {// Quando o inventário estiver cheio, aguarde o "consumidor" para consumir o produto. while (tamanho> = capacidade) fullcondtion.await (); // Obtenha "Quantidade real de produção" (ou seja, a nova quantidade adicionada no inventário) // se "inventário" + "quantidade desejada de produção"> "Capacidade total", então "incremento real" = "capacidade total" - "capacidade de corrente". (Preencha o armazém no momento) // Caso contrário, "incremento real" = "a quantidade que você deseja produzir" int inc = (tamanho+esquerda)> capacidade? (tamanho da capacidade): Esquerda; tamanho += inc; Esquerda -= Inc; System.out.printf ("%s Produce (%3D) -> esquerda =%3D, Inc =%3D, Size =%3D/N", Thread.CurrentThread (). GetName (), Val, Left, Inc, Size); // notifique o "consumidor" que você pode consumir. esvazio.signal (); }} catch (interruptEdException e) {} finalmente {Lock.unlock (); }} consumo público vazio (int val) {Lock.lock (); tente {// esquerda significa "a quantidade de consumo a ser consumida" (pode ser muito grande, o inventário não é suficiente, então você precisa consumir mais) int left = val; enquanto (esquerda> 0) {// Quando o inventário estiver 0, aguarde o "produtor" produzir o produto. while (size <= 0) esvazio.await (); // Obtenha "Quantidade de consumo real" (ou seja, a diminuição real no inventário) // se "inventário" <"A quantidade que o cliente deseja consumir", então "consumo real" = "inventário"; // Caso contrário, "consumo real" = "A quantidade que o cliente deseja consumir". int dec = (tamanho <esquerda)? Tamanho: Esquerda; tamanho -= dez; Esquerda -= DEC; System.out.printf ("%s consume (%3d) <- esquerda =%3d, dez =%3d, size =%3d/n", thread.currentThread (). GetName (), val, esquerda, dez, tamanho); FullCondtion.signal (); }} catch (interruptEdException e) {} finalmente {Lock.unlock (); }} public string tostring () {return "capacidade:"+capacidade+", tamanho real:"+tamanho; }}; // Produtor Classe Produtor {Depósito de Depósito Privado; Produtor público (depósito depósito) {this.depot = depósito; } // Produto do consumidor: Crie um novo thread para produzir o produto no armazém. public void Produce (final int val) {new Thread () {public void run () {deposit.produce (val); } }.começar(); }} // Classe de consumidor CLIENTE {private depósito depósito; cliente público (depósito depósito) {this.depot = depósito; } // Produto do consumidor: Crie um novo thread para consumir o produto no armazém. public void Consumo (final int val) {new Thread () {public void run () {depot.consume (val); } }.começar(); }} classe pública Locktest3 {public static void main (string [] args) {depot mdepot = new Depot (100); Produtor MPro = Novo Produtor (MDepot); Cliente mcus = novo cliente (mdepot); mpro.produce (60); mpro.produce (120); mcus.consume (90); mcus.consume (150); mpro.produce (110); }} (Uma vez) resultado:
Thread-0 Produce (60)-> Esquerda = 0, Inc = 60, tamanho = 60Thread-1 Produce (120)-> esquerda = 80, inc = 40, tamanho = 100THRead-2 consumo (90) <-esquerda = 0, dez = 90, tamanho = 10THRAD-3 Consumo (150) <-esquerdo = 140, 100, 10, tamanho = 10THREAD-4 consumo (150) <-esquerda = 40, dez = 100, tamanho = 0Thread-4 Produce (110)-> esquerda = 0, inc = 10, tamanho = 10Thread-3 consome (150) <-esquerda = 30, dec = 10, tamanho = 0THRead-1 Produce (120)-> esquerda = 0, 80, tamanho = 80TheReaded-3 (150) <150) <120)-> esquerda = 0, 80, tamanho = 80TheReadel-3 30)