Siete tipos de cola de bloqueo

  La cola delante de nuestros contactos son sin bloqueo de cola, como PriorityQueue, LinkedList (LinkedList es una lista doblemente enlazada, que implementa la interfaz quitar de la cola).

  Cuando se utiliza sin bloqueo de cola hay una gran pregunta es la siguiente: no va a obstruir el flujo actual, entonces el consumidor en la cara de similares - cuando el modelo de productor, es necesario aplicar las políticas y la sincronización entre hilos adicionales despierta la política, esta es muy complicado de implementar. Pero con el bloqueo de cola no es el caso, se produciría el bloqueo del subproceso actual, tales como elementos de toma de hilo de una cola de bloqueo vacío, en cuyo caso se bloqueará el hilo hasta que la cola de bloqueo con los elementos. Cuando hay elementos en la cola, hilo bloqueado se activará automáticamente (no es necesario para escribir el código de despertar). Esto proporciona una gran comodidad.

1. ¿Qué es el bloqueo de cola
   cola de bloqueo (BlockingQueue) es un soporte para la cola dos operaciones adicionales. Estos método de dos inserción apoyo operativo adicional y la eliminación de la obstrucción.
  1) soportes de bloqueo método de inserción: significa que cuando la cola está llena, la cola de bloqueará el hilo inserto elementos hasta la insatisfacción cola.
  2) soportes de bloqueo método de eliminación: medios que la cola está vacía, obtenga el elementos de rosca esperarán a la cola para ser no vacío.
  El bloqueo de cola utilizado comúnmente en los productores y consumidores de la escena, se añade el elemento de hilo productor a la cola, el consumidor es el hilo a tomar elementos de la cola.

1. cola sin bloqueo en varios métodos principales:

  añadir (E e): el elemento se inserta en el extremo de la cola de correo, si se ha insertado con éxito, devuelve true; si falla la inserción (es decir, la cola está llena), se producirá una excepción;

  eliminar (): quitar la cabeza de los elementos de la cola eliminadas si tiene éxito, devuelve true; si no la eliminación (la cola está vacía), se produce una excepción;

  oferta (E e): el elemento se inserta en el extremo de la cola de correo, si se ha insertado con éxito, devuelve true; si falla la inserción (es decir, la cola está llena), se devuelve false;

  poll (): eliminar el equipo y obtener el primer elemento, si tiene éxito, se devuelve la cabeza del elemento de la cola, de lo contrario devolver null;

  peek (): Obtiene el primer elemento del equipo, si tiene éxito, se devuelve la cabeza del elemento de la cola, de lo contrario nula

  Sin bloqueo de cola, generalmente se recomienda utilizar tres métodos ofrecen, sondeo y PEEK, añadir y eliminar métodos obsoletos. Dado que el uso de tres métodos ofrecen, sondeo y el valor peek está determinada por la operación de retorno éxito, el uso de agregar y quitar un método no puede lograr tales resultados. Tenga en cuenta que el método de cola de no bloqueo no están sincronizados medidas.

2. La cola de bloqueo en varios métodos principales:

  El bloqueo de cola incluye la mayor parte de los métodos de la no-bloqueo de cola, cinco métodos mencionados anteriormente están presentes en la cola de bloqueo, pero tenga en cuenta que estos cinco métodos en los que bloquean la cola se han sincronizado medidas. Además, la cola de bloqueo proporciona otro método muy útil de cuatro:

  poner (E e): método put elemento se utiliza para depositar la cola, si la cola está llena, a la espera;

  tomar (): tomar el primer método se utiliza para tomar los elementos del equipo, si la cola está vacía, a la espera;

  oferta (E e, largo tiempo de espera, la unidad de TimeUnit): Oferta método de elementos se utiliza para depositar la cola, si la cola está llena, a continuación, espera durante un cierto tiempo, cuando se alcanza el límite de tiempo si no se ha insertado con éxito, devuelve falso, de lo contrario return true;

  poll (largo tiempo de espera, la unidad de TimeUnit): método de sondeo se utiliza para tomar elementos del primer equipo, si la cola está vacía, entonces esperar un cierto período de tiempo, cuando se alcanza el límite de tiempo si tomar, o nula, de lo contrario elemento hecho;

 
  El bloqueo de cola se utiliza para almacenar elementos productores, los consumidores utilizan para obtener el elemento contenedor. Cuando el bloqueo de cola no está disponible, cuatro tipos de tratamientos proporcionados por estas dos operaciones adicionales, como se muestra en la tabla.
  
  • Lanza una excepción: cuando la cola está llena, si vas a la cola para insertar elementos, lanza IllegalStateException ( "QueueFull") es una excepción. Cuando la cola está vacía cuando se recupera un elemento de la cola NoSuchElementException será una excepción.
  • Devuelve el valor especial: Cuando se inserta un elemento en la cola, devuelve el elemento se inserta el éxito, devuelve verdadero si tiene éxito, de lo contrario devuelve false. Si el método es eliminar, un elemento se elimina de la cola, se devuelve el elemento si se devuelve no nulo.
  • Se ha bloqueado: Cuando el bloqueo de cola está llena, si el hilo productor para poner la cola de elemento, la cola se bloqueará hasta que el hilo productor hasta que la cola está disponible o no responde a abortar. Cuando la cola está vacía, si el hilo consumidor toma elementos de la cola, la cola se bloqueará hilo consumidor en vivo hasta que la cola no está vacía.
  • Tiempo de espera de salida: Cuando el bloqueo de la cola está llena, si el hilo productor insertar elementos en la cola, la cola bloqueará el hilo productor desde hace algún tiempo, si hay más de un tiempo determinado, el hilo termina productor.

 

II. Siete cola de bloqueo importante

  Desde Java 1.5, ofertas cola de bloqueo en varias paquete java.util.concurrent, principalmente en lo siguiente:

  1.ArrayBlockingQueue: Sobre la base de la matriz para lograr una cola de bloqueo delimitada que interna mantiene una datos de longitud fija de tampón de cola (la cola consiste en una matriz), los elementos de esta cola se ordenan de acuerdo con el principio de primero en primero en salir (FIFO), y debe especificar el tamaño al crear objetos ArrayBlockingQueue capacidad . también ArrayBlockingQueue mantenido dentro de dos variables enteras, identificar respectivamente la localización de la cabeza de la cola y la cola de la matriz.

  Y también puede especificar no la equidad y la justicia, la feria no predeterminada. El llamado un acceso justo a los medios de colas que el hilo bloqueado, puede seguir la cola de acceso e impida el normal, que es para bloquear el hilo para acceder a la cola. La injusticia es un hilo que esperar a que no justo cuando la cola está disponible, el hilo bloqueado puede ser elegible para competir por el acceso a la cola, es posible acceder por fin el hilo bloqueado cola. Con el fin de asegurar la equidad, generalmente disminuye el rendimiento. Podemos crear un equipo de utilización igualdad de condiciones bloqueo de código de la siguiente columna.

 

ArrayBlockingQueue fairQueue = new ArrayBlockingQueue(1000,true);
public ArrayBlockingQueue(int capacity, boolean fair) { 
  if (capacity <= 0) throw new IllegalArgumentException();
  this.items = new Object[capacity];
  lock = new ReentrantLock(fair); //可以看出访问者的公平性是使用可重入锁实现的
  notEmpty = lock.newCondition();
  notFull = lock.newCondition();
}

  2.LinkedBlockingQueue:基于链表实现的一个有界阻塞队列,内部维持着一个数据缓冲队列(该队列由链表构成),此队列按照先进先出的原则对元素进行排序。当生产者往队列中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回;只有当队列缓冲区达到最大值缓存容量时(可以通过LinkedBlockingQueue的构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程将会被唤醒,反之对于消费者这端的处理也基于同样的原理。在创建LinkedBlockingQueue对象时如果不指定容量大小,则默认大小为Integer.MAX_VALUE。这样的话,如果生产者的速度一旦大于消费者的速度,也许还没有等到队列满阻塞产生,系统内存就有可能已经被消耗殆尽了。

  LinkedBlockingQueue之所以能够高效的处理并发数据,是因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。

  3.PriorityBlockingQueue:支持优先级排序的无界阻塞队列,以上2种队列都是先进先出队列,而PriorityBlockingQueue却不是,它会按照元素的优先级对元素进行排序,默认情况下元素采取自然顺序排列,也可以通过构造函数传入的Compator对象来决定。并且也是按照优先级顺序出队,每次出队的元素都是优先级最高的元素。在实现PriorityBlockingQueue时,内部控制线程同步的锁采用的是公平锁。需要注意的是PriorityBlockingQueue并不会阻塞数据生产者,而只是在没有可消费的数据时阻塞数据的消费者,因此使用的时候要特别注意,生产者生产数据的速度绝对不能快于消费者消费数据的速度,否则时间一长,会最终耗尽所有的可用堆内存空间。注意,此阻塞队列为无界阻塞队列,即容量没有上限(通过源码就可以知道,它没有容器满的信号标志)。

  4.DelayQueue:基于PriorityQueue,一种支持延时的获取元素的无界阻塞队列,DelayQueue中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue也是一个无界队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞。

   5.SynchronousQueue:一个不存储元素的阻塞队列。每一个put操作必须等待一个take操作,否则不能继续添加元素。 可以认为SynchronousQueue是一个缓存值为1的阻塞队列,但是SynchronousQueue内部并没有数据缓存空间,数据是在配对的生产者和消费者线程之间直接传递的。可以这样来理解:SynchronousQueue是一个传球手,SynchronousQueue不存储数据元素,队列头元素是第一个排队要插入数据的线程,而不是要交换的数据,SynchronousQueue负责把生产者线程处理的数据直接传递给消费者线程,生产者和消费者互相等待对方,握手,然后一起离开。它支持公平访问队列。默认情况下线程采用非公平性策略访问队列。在创建公平性访问的SynchronousQueue,如果设置为true,则等待的线程会采用先进先出的顺序访问队列。
   6.LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。相对于其他阻塞队列,LinkedTransferQueue多了tryTransfer和transfer方法。

  transfer()方法:如果当前有消费者正在等待接收元素(消费者使用take()方法或带时间限制的poll()方法),transfer()方法可以把生产者传入的元素立刻传输给消费者;如果没有消费者在等待接收元素,transfer()方法会将元素存放到队列的tail节点,并等到该元素被消费者消费了才返回。

  transfer()方法的关键代码如下:

Node pred = tryAppend(s, haveData);
return awaitMatch(s, pred, e, (how == TIMED), nanos);

  第一行代码是试图把存放当前元素的s节点作为tail节点,第二行代码是让CPU自旋等待消费者消费元素。因为自旋会消耗CPU,所以自旋一定的次数后使用Thread.yield()方法来暂停当前正在执行的线程,并执行其他线程。

  tryTransfer()方法:该方法是用来试探生产者传入的元素是否能直接传给消费者,如果没有消费者等待接收元素,则返回false。与transfer()方法的区别:tryTransfer()方法是立即返回(无论消费者是否接收),transfer()方法是必须等到消费者消费了才返回。对于带有时间限制的tryTransfer(E e, long timeout, TimeUnit unit)方法,则是试图把生产者传入的元素直接传给消费者,但是如果没有消费者消费该元素则等待指定的时间之后再返回,如果超时还没消费元素,则返回false,如果在超时时间内消费了元素,则返回true。

   7.LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。LinkedBlockingDeque是一个由链表结构组成的双向阻塞队列。所谓双向队列指的是可以从队列的两端插入和移出元素。双向队列因为多了一个操作队列的入口,在多线程同时入队时,也就减少了一半的竞争。相比其他的阻塞队列,LinkedBlockingDeque多了addFirst、addLast、offerFirst、offerLast、peekFirst和peekLast等方法,以First单词结尾的方法,表示插入、获取(peek)或移除双端队列的第一个元素。以Last单词结尾的方法,表示插入、获取或移除双端队列的最后一个元素。另外,插入方法add等同于addLast,移除方法remove等效于removeFirst。
 
三.阻塞队列的实现原理
   本文以ArrayBlockingQueue为例,其他阻塞队列实现原理可能和ArrayBlockingQueue有一些差别,但是大体思路应该类似,有兴趣的朋友可自行查看其他阻塞队列的实现源码。
  首先看一下ArrayBlockingQueue类中的几个成员变量:
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {
    /**
     * Serialization ID. This class relies on default serialization
     * even for the items array, which is default-serialized, even if
     * it is empty. Otherwise it could not be declared final, which is
     * necessary here.
     */
    private static final long serialVersionUID = -817911632652898426L;
    /** The queued items */
    final Object[] items;
    /** items index for next take, poll, peek or remove */
    int takeIndex;
    /** items index for next put, offer, or add */
    int putIndex;
    /** Number of elements in the queue */
    int count;
    /** Main lock guarding all access */
    final ReentrantLock lock;
    /** Condition for waiting takes */
    private final Condition notEmpty;
    /** Condition for waiting puts */
    private final Condition notFull;
    transient Itrs itrs = null;

 

  可以看出,ArrayBlockingQueue中用来存储元素的实际上是一个数组,takeIndex和putIndex分别表示队首元素和队尾元素的下标,count表示队列中元素的个数。 lock是一个可重入锁,notEmpty和notFull是等待条件。
  从上述代码中我们可知,如果队列是空的,消费者会一直等待,当生产者添加元素时,生产者是使用Condition线程间通信的方法来通知另一个消费者线程的当生者往列里添加元素会阻塞住生产者,当消费者消费了一个队列中的元素后,会通知生产者当前队列可用。那具体是怎么通知的的?我们可详细看分析下下面几个方法。
public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();//操作之前先上锁
        try {
            while (count == items.length)//当队列满了
                notFull.await();   //则生产者不继续添加,而是将自己阻塞,直到有消费者来消费并将自己唤醒后,才可以继续执行
            enqueue(e);
        } finally {
            lock.unlock();  //释放锁
        }
    }
private void enqueue(E x) {//相当于add()方法
    final Object[] items = this.items;
    items[putIndex] = x;//在队尾添加元素
    if (++putIndex == items.length)//索引自增,如果已是最后一个位置,重新设置 putIndex = 0
     putIndex = 0;
    count++;
    notEmpty.signal();
}
public E take() throws InterruptedException {//由于此时并发容器已满,所以生产者生产失败,释放了锁,轮到消费者执行
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly(); //操作前先上锁
        try {
            while (count == 0)//判断容器不为空
                notEmpty.await();
            return dequeue();//调用该方法
        } finally {
            lock.unlock();
        }
    }
private E dequeue() {//相当于remove()
    final Object[] items = this.items;//获取数组容器
    E x = (E) items[takeIndex];//获取队首元素,因为ArrayBlockingQueue是先进先出队列
    items[takeIndex] = null;//将该位置置空
    if (++takeIndex == items.length)//索引自增,如果已是最后一个位置,重新设置 putIndex = 0
     takeIndex = 0;
    count--;//将容器中元素个数减一
    if (itrs != null)
        itrs.elementDequeued();
    notFull.signal();//唤醒其他被阻塞的线程,由于刚才生产者因容器已满而被阻塞掉,这时候就会被该线程唤醒了,唤醒之后就可继续它的生产工作。
    return x;
}

 

 
   从put方法的实现可以看出,它先获取了锁,并且获取的是可中断锁,然后判断当前元素个数是否等于数组的长度,如果相等,表示 队列元素已满, 调用notFull.await()进行等待, 那么当前线程将会被notFull条件对象挂起加到等待队列中,直到队列有空位才会唤醒执行添加操作。但如果队列没有满,那么就直接调用enqueue(e)方法将元素加入到数组队列中。调用tack()方法也是同样的原理。
 
 
 
,所 公平 访问队 列是指阻塞的 线 程,可以按照
阻塞的先后 访问队 列,即先阻塞 线 程先 访问队 列。非公平性是 先等待的 线 程是非公平
的,当 列可用 ,阻塞的 线 程都可以争 夺访问队 列的 格,有可能先阻塞的 线 程最后才 访问
列。 了保 公平性,通常会降低吞吐量。我 可以使用以下代 码创 建一个公平的阻塞
列。

 

Supongo que te gusta

Origin www.cnblogs.com/ljl150/p/12628300.html
Recomendado
Clasificación