O sincronizador de fila AbstractQueuedSynchronizer é usado para construir uma estrutura básica para bloqueios ou outros componentes de sincronização.Ele usa uma variável de membro int para representar o status de sincronização e completa o enfileiramento de threads de aquisição de recursos por meio da fila FIFO embutida. Podemos examinar o código-fonte de bloqueios reentrantes, bloqueios de leitura e gravação ou semáforos, CountDownLatch e outras fontes apresentadas anteriormente. Você descobrirá que sempre há uma classe interna estática em seu código-fonte que herda AbstractQueuedSynchronizer. O exemplo de código é o seguinte:
static class Sync extends AbstractQueuedSynchronizer {
//具体逻辑
}
A principal maneira de usar o sincronizador é a herança. As subclasses gerenciam o estado de sincronização herdando o sincronizador e implementando seus métodos abstratos. Em AbstractQueuedSynchronizer, há um estado variável que identifica o estado de sincronização e fornece getState (), setState (int newState) e compareAndSetState (int expect, int update)) Três métodos para operar o estado síncrono. Eles podem garantir que a mudança de estado é segura.
O sincronizador é projetado com base no padrão de método de modelo, ou seja, o usuário precisa herdar o sincronizador e reescrever o método especificado, em seguida, combinar o sincronizador na implementação do componente de sincronização personalizado e chamar o método de modelo fornecido pelo sincronizador. E esses métodos de modelo chamarão o método substituído pelo usuário. A seguir está o método de modelo fornecido pelo sincronizador:
Nome do método |
Descrição |
void adquirir (int arg) |
Aquisição exclusiva do status de sincronização, se a thread atual adquirir o status com sucesso, este método retornará, caso contrário, ele entrará na fila de sincronização para esperar, este método irá chamar o método tryAcquire (iny arg) substituído |
void absorbInterruptibly (int arg) |
O mesmo que adquirir (int arg), mas este método responderá à interrupção. O thread atual não obtém o status de sincronização e entra na fila de sincronização. Se o thread atual for interrompido, um InterruptedException será lançado.
|
boolean tryAcquireNanos (int arg, long nanos) |
O limite de tempo limite é aumentado com base em takeInterruptibly (int arg). Se o estado de sincronização não for obtido dentro do tempo especificado, ele retornará falso, caso contrário, retornará verdadeiro.
|
void absorbShare (int arg) |
A diferença entre o acesso compartilhado ao status de sincronização e o acesso exclusivo é que vários threads podem obter o status de sincronização ao mesmo tempo. Irá chamar o método rewrite tryAcquireShared |
void activateShareInterruptibly (int arg) |
O mesmo que adquirirShare (int arg), mas este método responderá à interrupção |
boolean tryAcquireSharedNanos (int arg, long nanos) |
O limite de tempo limite é aumentado com base em takeShareInterruptibly (int arg). Se o estado de sincronização não for obtido dentro do tempo especificado, ele retornará falso, caso contrário, retornará verdadeiro. |
lançamento booleano (int arg) |
Liberar exclusivamente o estado de sincronização. Este método irá despertar o thread contido no primeiro nó na fila de sincronização após liberar o estado de sincronização. Chamará o método rewrite tryRelease |
boolean releaseShared (int arg) |
Estado de sincronização de liberação compartilhada, chamará o método de substituição tryReleaseShared |
Coleção <Thread> getQueueThreads () |
Obtenha a coleção de tópicos na fila de espera |
Como dissemos antes, o método do modelo será chamado e substituiremos o método substituído. Vamos dar uma olhada nos métodos fornecidos pelo sincronizador que precisamos substituir. A tabela a seguir mostra os métodos que o sincronizador pode substituir:
Nome do método |
Descrição |
protected boolean tryAcquire (int arg) |
Obtenha o status de sincronização exclusivamente. Para implementar este método, você precisa consultar o status atual e determinar se o status de sincronização atende às expectativas, e então executar CAS para definir o status de sincronização |
protected boolean tryRelease (int arg) |
Liberar exclusivamente o estado de sincronização, threads aguardando para obter o estado de sincronização terão a oportunidade de obter o estado de sincronização |
protegido int tryAcquireShared (int arg) |
Acesso compartilhado ao status de sincronização, retorna um valor maior que 0 para indicar sucesso, caso contrário, a aquisição falha. |
protected boolean tryReleaseShared (int arg) |
Estado de sincronização de liberação compartilhada. |
protegido booleano isHeldExclusively () |
Se o sincronizador atual está ocupado por uma thread em modo exclusivo, geralmente este método indica se ele está ocupado exclusivamente pela thread atual |
Acima, apresentamos o método de modelo e o método regravável do sincronizador. A partir do método, podemos ver que os métodos de modelo fornecidos pelo sincronizador são basicamente divididos em três categorias: aquisição exclusiva e estado de sincronização de liberação, aquisição compartilhada e estado de sincronização de liberação e sincronização de consulta O status de threads em espera na fila. O componente de sincronização personalizado usará o método de modelo fornecido pelo sincronizador para implementar sua própria semântica de sincronização. O seguinte usará um exemplo de bloqueio exclusivo para aprender mais sobre o uso de sincronizadores. Em primeiro lugar, nosso último modelo, semelhante às ferramentas fornecidas pelo JUC, escreve um sincronizador de herança de classe interna.
public class MutexLock{
private final Sync sync = new Sync();
//内部类继承同步器
private static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1L;
}
//获取锁
public void lock() {
}
//可中断获取锁
public void lockInterruptibly() throws InterruptedException {
}
//尝试获取锁
public boolean tryLock() {
return false;
}
//可超时尝试获取锁
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
//释放锁
public void unlock() {
}
}
Conforme mostrado no código acima, é um modelo escrito sem qualquer implementação e, abaixo, precisamos usar o método de modelo fornecido pelo sincronizador e o método regravável para obter o bloqueio exclusivo acima. Este é apenas um exemplo simples de aprendizagem. Depois de aprender o princípio de realização do sincronizador, ferramentas de sincronização mais complexas serão escritas. Orientamos o sincronizador através do método de modelo acima para fornecer três métodos para adquirir bloqueios e um método para liberar bloqueios. Podemos implementar o seguinte código lógico da seguinte maneira:
public class MutexLock{
private final Sync sync = new Sync();
private static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1L;
}
public void lock() {
//调用同步器的方法获取锁
sync.acquire(1);
}
public void lockInterruptibly() throws InterruptedException {
//调用同步器的方法可中断获取锁
sync.acquireInterruptibly(1);
}
public boolean tryLock() {
return false;
}
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
//默认传入的是nanoseconds,不再做时间的转换
return sync.tryAcquireNanos(1, time);
}
public void unlock() {
//调用同步器的方法释放锁
sync.release(1);
}
}
Anteriormente, dissemos que void occur (int arg) chamará o método reescrito tryAcquire (iny arg), portanto, precisamos reescrever o método tryAcquire (iny arg) para alterar o estado do sincronizador. Se tiver êxito, adquira o bloqueio. Se falhar, adicione-o à fila de espera. A implementação do código a seguir.
private static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1L;
public boolean tryAcquire(int acquires) {
//如果将状态改变为1,如果成功则获取到锁
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
//释放锁
protected boolean tryRelease(int releases) {
if (getState() == 0)
throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
}
}
Este blog é principalmente para explicar o uso básico do sincronizador AbstractQueuedSynchronizer e para entender os métodos de modelo e métodos regraváveis fornecidos por AbstractQueuedSynchronizer. Podemos usar AbstractQueuedSynchronizer para construir rapidamente um componente de sincronização. Analisaremos o princípio de implementação de AbstractQueuedSynchronizer em detalhes posteriormente.