并发队列的介绍及使用

在JDK1.5新加入了一个包concurrent,位于java.util.concurrent。在我们写业务代码的时候,可能最为常见就是ConcurrentHashMap。当然今天我们的主角不是他,而是queue。在并发队列上JDK提供了两套实现。

阻塞队列(IO):以BlockingQueue接口为代表的阻塞队列。
非阻塞(NIO)队列:以ConcurrentLinkedQueue为代表的高性能队列。

1.阻塞队列(IO)

1.1.什么是阻塞队列

1、当队列是满时,往队列里添加元素的操作会被阻塞;
2、或者当队列是空的时,从队列中获取元素的操作将会被阻塞;
3、阻塞队列是线程安全的。

1.2.阻塞队列的应用场景

阻塞队列常用于生产者和消费者的场景。

1、生产者是往队列里添加元素的线程;
2、消费者是从队列里拿元素的线程;
3、阻塞队列就是生产者存放元素的容器。

1.3.BlockingQueue

1.3.1.ArrayBlockingQueue

1、是一个有边界的阻塞队列,它的内部实现是一个数组。有边界的意思是它的容量是有限的,我们必须在其初始化的时候指定它的容量大小,容量大小一旦指定就不可改变。
2、采用FIFO先进先出的方式存储数据,最新插入的对象是尾部,最新移出的对象是头部。

1.3.2.ArrayBlockingQueue

1、LinkedBlockingQueue阻塞队列大小的配置是可选的,如果我们初始化时指定一个大小,它就是有边界的,如果不指定,它就是无边界的。说是无边界,其实是采用了默认大小为Integer.MAX_VALUE的容量 。它的内部实现是一个链表。
2、和ArrayBlockingQueue一样,LinkedBlockingQueue 也是以先进先出的方式存储数据,最新插入的对象是尾部,最新移出的对象是头部。

1.3.3.PriorityBlockingQueue

1、是一个没有边界的队列,它的排序规则和 java.util.PriorityQueue一样
2、需要注意,PriorityBlockingQueue中允许插入null对象。
3、所有插入PriorityBlockingQueue的对象必须实现 java.lang.Comparable接口,队列优先级的排序规则就是按照我们对这个接口的实现来定义的。
4、我们可以从PriorityBlockingQueue获得一个迭代器Iterator,但这个迭代器并不保证按照优先级顺序进行迭代。

1.3.4.SynchronousQueue

仅允许容纳一个元素。当一个线程插入一个元素后会被阻塞,除非这个元素被另一个线程消费。

2.非阻塞(NIO)队列

和阻塞队列相比,他不会被阻塞,并且效率比阻塞高。

2.1.ConcurrentLinkedQueue

ConcurrentLinkedQueue是一个适用于高并发场景下的队列,通过无锁的方式,实现了高并发状态下的高性能。通常ConcurrentLinkedQueue性能好于BlockingQueue。它是一个基于链接节点的无界线程安全队列。
该队列的元素遵循先进先出的原则。头是最先加入的,尾是最近加入的,该队列不允许null元素。

2.2.1重要方法

add 和offer() 都是加入元素的方法(在ConcurrentLinkedQueue中这俩个方法没有任何区别),在这两个方法中,均调用offerLast(E e)方法。
将指定的元素插入此双端队列的末尾

  /**
     * Inserts the specified element at the tail of this deque.
     * As the deque is unbounded, this method will never throw
     * {@link IllegalStateException} or return {@code false}.
     *
     * @return {@code true} (as specified by {@link Collection#add})
     * @throws NullPointerException if the specified element is null
     */
    public boolean add(E e) {
        return offerLast(e);
    }
       /**
     * Inserts the specified element at the tail of this deque.
     * As the deque is unbounded, this method will never return {@code false}.
     *
     * @return {@code true} (as specified by {@link Queue#offer})
     * @throws NullPointerException if the specified element is null
     */
    public boolean offer(E e) {
        return offerLast(e);
    }

poll() 和peek() 都是取头元素节点,区别在于前者会删除元素,后者不会。

    public E poll()           { return pollFirst(); }
    public E peek()           { return peekFirst(); }

返回删除此双端队列的第一个元素

/**
* Retrieves and removes the first element of this deque,
* or returns {@code null} if this deque is empty.
*
* @return the head of this deque, or {@code null} if this deque is empty
*/
public E pollFirst() {
        restart: for (;;) {
            for (Node<E> first = first(), p = first;;) {
                final E item;
                if ((item = p.item) != null) {
                    // recheck for linearizability
                    if (first.prev != null) continue restart;
                    if (ITEM.compareAndSet(p, item, null)) {
                        unlink(p);
                        return item;
                    }
                }
                if (p == (p = p.next)) continue restart;
                if (p == null) {
                    if (first.prev != null) continue restart;
                    return null;
                }
            }
        }
    }

返回此双端队列的第一个元素

/**
* Retrieves, but does not remove, the first element of this deque,
* or returns {@code null} if this deque is empty.
*
* @return the head of this deque, or {@code null} if this deque is empty
*/
public E peekFirst() {
	restart: for (;;) {
		E item;
		Node<E> first = first(), p = first;
		while ((item = p.item) == null) {
			if (p == (p = p.next)) continue restart;
                if (p == null)
                    break;
       	}	
	    // recheck for linearizability
       	if (first.prev != null) continue restart;
        return item;
	}
}

2.2.2代码演示

@Test
	public void test() throws Exception {
		ConcurrentLinkedDeque<String> qStr = new ConcurrentLinkedDeque<>();
		qStr.add("1胡涛");
		qStr.add("2谢天");
		qStr.add("3杨腾龙");
		System.out.println("当前队列:"+qStr.toString());
		String poll2 = qStr.peek();
		System.out.println("调用peek()方法获取到"+poll2);
		System.out.println("调用poll()方法后,队列还有:"+qStr.toString());
		String poll = qStr.poll();
		System.out.println("调用poll()方法获取到"+poll);
		System.out.println("调用poll()方法后,队列还有:"+qStr.toString());
	}

在这里插入图片描述

3.阻塞队列的生产消费者应用

基础知识储备:你需要多线程的知识点,这里我就不啰嗦多线程的知识点了。
AtomicInteger:高并发的情况下,i++无法保证原子性,往往会出现问题,所以引入AtomicInteger类。
volatile:1、保证共享变量对所有的线程的可见;2、禁止CPU进行重排序
生产者

class ProducerThread implements Runnable {
	
	private BlockingQueue<String> blockingQueue;
	
	private AtomicInteger count = new AtomicInteger();
	
	private volatile boolean FLAG = true;

	public ProducerThread(BlockingQueue<String> blockingQueue) {
		this.blockingQueue = blockingQueue;
	}

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName() + "生产者开始启动....");
		while (FLAG) {
			String data = count.incrementAndGet() + "";
			try {
				boolean offer = blockingQueue.offer(data, 2, TimeUnit.SECONDS);
				if (offer) {
					System.out.println(Thread.currentThread().getName() + ",生产队列" + data + "成功..");
				} else {
					System.out.println(Thread.currentThread().getName() + ",生产队列" + data + "失败..");
				}
				Thread.sleep(1000);
			} catch (Exception e) {

			}
		}
		System.out.println(Thread.currentThread().getName() + ",生产者线程停止...");
	}

	public void stop() {
		this.FLAG = false;
	}

}

消费者

class ConsumerThread implements Runnable {
	private volatile boolean FLAG = true;
	private BlockingQueue<String> blockingQueue;

	public ConsumerThread(BlockingQueue<String> blockingQueue) {
		this.blockingQueue = blockingQueue;
	}

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName() + "消费者开始启动....");
		while (FLAG) {
			try {
				String data = blockingQueue.poll(2, TimeUnit.SECONDS);
				if (data == null || data == "") {
					FLAG = false;
					System.out.println("消费者超过2秒时间未获取到消息.");
					return;
				}
				System.out.println("消费者获取到队列信息成功,data:" + data);

			} catch (Exception e) {
				// TODO: handle exception
			}
		}
	}
}
@Test
	public void proDucerAndConsumer() throws Exception {
		BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>(3);
		ProducerThread producerThread = new ProducerThread(blockingQueue);
		ConsumerThread consumerThread = new ConsumerThread(blockingQueue);
		Thread t1 = new Thread(producerThread);
		Thread t2 = new Thread(consumerThread);
		t1.start();
		t2.start();
		//10秒后 停止线程..
		try {
			Thread.sleep(10*1000);
			producerThread.stop();
		} catch (Exception e) {
			// TODO: handle exception
		}

	}

运行效果
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章