佇列、阻塞佇列

z1340954953發表於2018-05-08

佇列

佇列是一種先入先出的資料結構,新加入的元素都是加入到佇列的後面

阻塞佇列

java.util.concurrent.BlockingQueue<E>,是一種支援阻塞的插入元素,阻塞的移除元素操作的佇列。

阻塞的插入:當佇列空間滿了,阻塞執行緒繼續向佇列中新增元素

阻塞的移除:當佇列空間空了,阻塞執行緒從佇列中取出元素

從這裡看出,阻塞佇列可以用在生產者/消費者模型中,生產者是向佇列中新增元素的執行緒,消費者是從佇列中取出元素的執行緒

*佇列的操作方法

阻塞佇列的插入刪除方法
方法丟擲異常返回特殊值一直阻塞超時退出
插入add(e)offer(e)put(e)offer(e,time,unit)
移除remove()polltakepoll(time,unit)
檢查方法element()peek()//

丟擲異常:指的是佇列滿了或者佇列為空,就丟擲異常

返回特殊值:指的是插入方法返回的是true/false,移除方法返回的是元素

一直阻塞:指的是如果佇列滿了,再呼叫方法向佇列中新增,生產者執行緒會一直阻塞,佇列空了,再從佇列中取元素,消費者執行緒會一直阻塞

超時退出:同樣是對於上面的情況,來說,不過是超出了時間,執行緒就會退出

* 如果是無界佇列,插入的put,offer一定不會阻塞,而且offer返回true

阻塞佇列BlockQueue的實現類

1 . ArrayBlockingQueue

public class ArrayBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

    /** 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;
	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 . LinkedBlockingDeque

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {
   
    /**
     * Linked list node class
     */
    static class Node<E> {
        E item;

        Node<E> next;

        Node(E x) { item = x; }
    }

    /** The capacity bound, or Integer.MAX_VALUE if none */
    private final int capacity;

    /** Current number of elements */
    private final AtomicInteger count = new AtomicInteger(0);

    /**
     * Head of linked list.
     * Invariant: head.item == null
     */
    private transient Node<E> head;

    /**
     * Tail of linked list.
     * Invariant: last.next == null
     */
    private transient Node<E> last;

    /** Lock held by take, poll, etc */
    private final ReentrantLock takeLock = new ReentrantLock();

    /** Wait queue for waiting takes */
    private final Condition notEmpty = takeLock.newCondition();

    /** Lock held by put, offer, etc */
    private final ReentrantLock putLock = new ReentrantLock();

    /** Wait queue for waiting puts */
    private final Condition notFull = putLock.newCondition();

    /**
     * Signals a waiting take. Called only from put/offer (which do not
     * otherwise ordinarily lock takeLock.)
     */
	 public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

    /**
     * Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity.
     *
     * @param capacity the capacity of this queue
     * @throws IllegalArgumentException if {@code capacity} is not greater
     *         than zero
     */
    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);
    }

   

* LinkedBlockingQueue的建立可以指定容量大小,預設是Integer.MAX_VALUE,可以認為是無界的

* 存放元素的結構,是以連結串列存放

* 生產者和消費者執行緒,持有的ReentrantLock是不同的,意味著生產者執行緒和消費者執行緒可以並行執行

,通過檢查count,完成阻塞消費者或者生產者的動作

3.SynchronousQueue 

和前面兩種佇列實現不同的,這個佇列本身不會去存放元素,每一個put操作,必須等待另一個take操作,否則就不能繼續新增元素

    /**
     * Creates a <tt>SynchronousQueue</tt> with nonfair access policy.
     */
    public SynchronousQueue() {
        this(false);
    }
	
    /**
     * Creates a <tt>SynchronousQueue</tt> with the specified fairness policy.
     *
     * @param fair if true, waiting threads contend in FIFO order for
     *        access; otherwise the order is unspecified.
     */
    public SynchronousQueue(boolean fair) {
        transferer = fair ? new TransferQueue() : new TransferStack();
    }

* 可以指定執行緒訪問的策略,如果是true,按照先進先出來訪問佇列,否則就是非公平的,預設是非公平的

等待通知機制,自定義一個阻塞佇列

使用陣列實現一個阻塞佇列 https://blog.csdn.net/ditto_zhou/article/details/77330733

使用阻塞佇列來實現生產者消費模型

package com.ftf.thread.lock;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

public class ProductCustomerDemo {
	private static AtomicInteger ai = new AtomicInteger();
	public static void main(String[] args) {
		final ArrayBlockingQueue<Integer> abq = new ArrayBlockingQueue<Integer>(10);
		Thread product = new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true){
					try {
						int a = ai.incrementAndGet();
						System.out.println("生產者生產資料:"+a);
						abq.put(a);
						Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				}
			}
		});
		Thread customer = new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true){
					try {
						int a = abq.take();
						System.out.println("消費者取資料"+a);
						Thread.sleep(5000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});
		product.start();
		customer.start();
	}
}

當達到容量上限制後,生產者停止生產資料,直到消費者消費,容量減少,繼續生產,在非單個生產者-消費者模型中,不用擔心出現執行緒假死的現象。



相關文章