A fila de bloqueio de pacotes simultâneos java juc-BlockQueue

Depois de aprender sobre a fila de sincronização do java, bloqueio e mecanismo de notificação de espera, olhar para a fila de bloqueio tornará mais fácil entender a fila de bloqueio. Uma fila de bloqueio é uma fila que oferece suporte a duas operações adicionais. Essas duas operações adicionais suportam a inserção de bloqueio: quando a fila está cheia, a fila irá bloquear a thread que insere o elemento até que a fila não esteja cheia; remoção de bloqueio: quando a fila está vazia, a thread que obtém o elemento irá esperar que a fila se torne vazia Quando a fila de bloqueio não está disponível, essas duas operações adicionais fornecem 4 métodos de processamento

Método / método de processamento

Lançar uma exceção

Devolver valor especial

Continua bloqueando

Tempo limite de saída

Método de inserção

adicionar (e)

oferta (e)

colocar (e)

oferta (e, tempo, unidade)

Método de remoção

retirar()

votação()

toma()

poll (tempo, unidade)

Método de inspeção

elemento()

olhadinha()

-

-

Lançar uma exceção significa que, quando a fila está cheia, se você inserir um elemento na fila, uma exceção IllegalStateException ("Queue cheia") será lançada. Quando a fila está vazia, obter elementos da fila lançará NoSuchElementException.

Retornar um valor especial significa que quando um elemento é inserido na fila, ele retornará se o elemento foi inserido com sucesso e retornará verdadeiro se for bem-sucedido. Se for um método de remoção, ele pegará um elemento da fila e retornará nulo se não for.

· Sempre bloqueando significa que quando a fila de bloqueio está cheia, se o encadeamento do produtor colocar elementos na fila, a fila continuará bloqueando o encadeamento do produtor até que a fila esteja disponível ou saia em resposta a uma interrupção. Quando a fila está vazia, se o encadeamento do consumidor tirar elementos da fila, a fila bloqueará o encadeamento do consumidor até que a fila não esteja vazia. Apresentaremos o código-fonte deste modo mais tarde.
· A saída de tempo limite significa que quando a fila de bloqueio está cheia, se o encadeamento do produtor inserir elementos na fila, a fila bloqueará o encadeamento do produtor por um período de tempo.Se o tempo especificado for excedido, o encadeamento do produtor será encerrado.

O JDK nos fornece várias filas de bloqueio. Seus métodos de implementação são quase os mesmos. Descreveremos a implementação de filas de bloqueio de uma maneira mais tarde. A tabela a seguir mostra as várias filas de bloqueio e descrições fornecidas pelo JDK:

Fila de bloqueio descrição
ArrayBlockingQueue Uma fila de bloqueio limitada composta por uma estrutura de array
LinkedBlockingQueue Uma fila de bloqueio limitada composta por uma estrutura de lista vinculada
PriorityBlockingQueue Uma fila de bloqueio ilimitada que suporta classificação de prioridade
DelayQueue Uma fila de bloqueio ilimitada implementada usando filas prioritárias
SynchronousQueue Uma fila de bloqueio que não armazena elementos
LinkedTransferQueue Uma fila de bloqueio ilimitada composta por uma estrutura de lista vinculada
LinkedBlockingDeque Uma fila de bloqueio bidirecional composta por uma estrutura de lista vinculada

Como acima, para as filas de bloqueio fornecidas por jdk para nós, se olharmos seu código-fonte, descobrimos que suas definições de variáveis ​​têm a interface Lock e a interface Condition como campos, ou seja, sua implementação é baseada em Lock e Condition. Contanto que dominemos o uso de Lock and Condition, é fácil dominar o uso de filas de bloqueio. Vamos pegar LinkedBlockingQueue como exemplo, e o código de definição é o seguinte:

/** Lock held by take, poll, etc */ take、poll操作时获取锁
private final ReentrantLock takeLock = new ReentrantLock();
/** Wait queue for waiting takes */等待take操作
private final Condition notEmpty = takeLock.newCondition();
/** Lock held by put, offer, etc */ put offer时获取锁
private final ReentrantLock putLock = new ReentrantLock();
/** Wait queue for waiting puts */ //等待put操作
private final Condition notFull = putLock.newCondition();

A seguir, temos que ver como LinkedBlockingQueue armazena e recupera elementos. Primeiro, tomamos o elemento de armazenamento como exemplo e tomamos o método put como exemplo. O código é o seguinte:

//唤醒一个take等操作的线程
private void signalNotEmpty() {
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lock();
        try {
            notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
}
 public void put(E e) throws InterruptedException {
        //如果传值为null,抛出异常
        if (e == null) throw new NullPointerException();
        int c = -1;
        //创建节点
        Node<E> node = new Node<E>(e);
        //获取putLock实例
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        //获取锁,可中断
        putLock.lockInterruptibly();
        try {
            //如果容量已满
            while (count.get() == capacity) {
                //存放元素的线程等待
                notFull.await();
            }
            //存入队列
            enqueue(node);
            c = count.getAndIncrement();
            //如果队列不满,唤醒存入队列的线程
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            //释放锁
            putLock.unlock();
        }
        //如果为0
        if (c == 0)
            //唤醒一个取出元素的线程
            signalNotEmpty();
    }

Introduzimos o código-fonte de armazenamento de elementos acima. A lógica de armazenamento de outros elementos é semelhante à anterior, por isso não os apresentaremos aqui. Vamos analisar o código-fonte para obter os elementos: método take. O código-fonte é o seguinte:

//唤醒一个put操作的线程
 private void signalNotFull() {
        final ReentrantLock putLock = this.putLock;
        putLock.lock();
        try {
            notFull.signal();
        } finally {
            putLock.unlock();
        }
} 
public E take() throws InterruptedException {
        E x;
        int c = -1;
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        //获取take锁
        takeLock.lockInterruptibly();
        try {
            //如果队列元素为空,获取线程等待
            while (count.get() == 0) {
                notEmpty.await();
            }
            x = dequeue();
            c = count.getAndDecrement();
            //如果队列元素不为空,唤醒取元素线程
            if (c > 1)
                notEmpty.signal();
        } finally {
            //释放锁
            takeLock.unlock();
        }
        //如果c=capacity 唤醒put操作的线程
        if (c == capacity)
            signalNotFull();
        return x;
    }

Acima, apresentamos as operações put e take de LinkedBlockingQueue. Podemos saber que o princípio principal da fila de bloqueio é aguardar o mecanismo de notificação, usando os componentes Lock, Condition e LockSupport fornecidos pelo pacote simultâneo. Desde que estejamos familiarizados com o uso desses três componentes, podemos implementar facilmente nossa própria fila de bloqueio. Se você quiser saber a implementação específica de outras filas de bloqueio, pode consultar o código-fonte por conta própria.O núcleo é o mecanismo de notificação de espera e os três componentes mencionados acima.

Acho que você gosta

Origin blog.csdn.net/wk19920726/article/details/108456825
Recomendado
Clasificación