java多线程学习(五)—— java并发队列

基本定义

1、在jdk1.5并发包中,对于并发队列提供了两套实现,一个是以 ConcurrentLinkedDeque为代表的高性能队列,它是无边界的,非阻塞式的队列;一个是以 BlockingQueue接口为代表的阻塞队列,它的主要实现类有:ArrayBlockingQueueLinkedBlockingQueueSynchronousQueue,但是他们二者都是继承自Queue接口。
2、有边界的含义:一个队列定义了大小,当队列中存储数据满了之后,再往队列中加入数据就会失败
3、阻塞队列的含义:当队列为空的时候,从队列中获取元素的线程就会进入阻塞状态,等待队列变为非空;当队列存满了的时候,往队列添加元素的线程就会进入阻塞状态,等待别的线程从队列中取数据。
4、阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。被阻塞的情况主要有以下两种:
1)当队列满了的时候进行入队列操作
2)当队列空了的时候进行出队列操作
因此,当一个线程试图对一个已经满了的队列进行入队列操作时,它将会被阻塞,除非有另一个线程做了出队列操作;同样,当一个线程试图对一个空队列进行出队列操作时,它将会被阻塞,除非有另一个线程进行了入队列操作。

ConcurrentLinkedDeque

ConcurrentLinkedDeque的重要方法:
1、add 和offer() 都是添加元素的方法,这里没有任何区别
2、poll() 和peek() 都是取队列头元素节点(队列遵循先进先出的原则,先进的元素处于队列头),区别在于前者会删除元素,后者不会

ConcurrentLinkedDeque<Object> concurrentLinkedDeque = new ConcurrentLinkedDeque<Object>();
concurrentLinkedDeque.add("张三");
concurrentLinkedDeque.offer("李四");
//增加元素,并且返回是否增加成功
boolean result = concurrentLinkedDeque.offer("王五");
System.out.println(concurrentLinkedDeque.size());
//取出队列头元素,并且删除该元素
System.out.println(concurrentLinkedDeque.poll());
System.out.println(concurrentLinkedDeque.size());
//取出队列头元素,但不删除元素
System.out.println(concurrentLinkedDeque.peek());
//往队列中添加 null 报错
concurrentLinkedDeque.offer(null);

注意:ConcurrentLinkedDeque队列中不能添加 null 元素

ArrayBlockingQueue

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

//有边界的阻塞队列,创建时必须指定大小
BlockingQueue<Object> queur = new ArrayBlockingQueue(3);
//add方法式非阻塞式的添加,当队列已经满了时,使用add方法添加元素就会直接报错
queur.add("张三");
queur.add("李四");
//在添加队列的时候进行阻塞操作,如果队列已经满了,则等待2s,2s后队列还是满的,则返回false,表示添加失败
boolean offer = queur.offer("王五", 2, TimeUnit.SECONDS);
System.out.println(offer);
//取出队列头元素,并且删除该元素
queur.poll();
//取出队列头元素,但不删除元素
queur.peek();
offer = queur.offer("王五麻子", 2, TimeUnit.SECONDS);
System.out.println(offer);
System.out.println(queur.size());
//在取队列头元素的时候,如果队列为null,则阻塞2s,2s之后队列还是null,则返回的object为null
Object object = queur.poll(2, TimeUnit.SECONDS);

注意:ArrayBlockingQueue队列中不能添加 null 元素

LinkedBlockingQueue

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

SynchronousQueue

SynchronousQueue队列内部仅允许容纳一个元素。当一个线程插入一个元素后会被阻塞,除非这个元素被另一个线程消费。
即SynchronousQueue的默认大小是 1,并且不能修改队列的大小;
其余操作和api和ArrayBlockingQueue队列一致。

实战例子

使用BlockingQueue模拟生产者与消费者

package com.dss.test20191520;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 生产者线程
 * 
 * @author asong
 */
class ProducerThread implements Runnable {

	// 多个生产者线程往同一个队列中添加元素
	private BlockingQueue<String> queue;
	// 执行一段时间之后,让生产者线程停止,这里需要在主线程中修改 flag 的值,增加 volatile 关键字,确保线程可见性
	private volatile boolean flag = true;
	// 多个生产者线程生产数据,使用原子类
	private static AtomicInteger count = new AtomicInteger();

	public ProducerThread(BlockingQueue queue) {
		this.queue = queue;
	}

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

	@Override
	public void run() {
		try {
			System.out.println("生产者线程启动。。。");
			while (flag) {
				System.out.println("生产者线程生产数据。。。");

				String data = count.incrementAndGet() + "";

				// 将数据存入队列
				boolean offer = queue.offer(data, 2, TimeUnit.SECONDS);
				if (offer) {
					System.out.println("生产者,存入" + data + "到队列中,成功。。。");
				} else {
					System.out.println("生产者,存入" + data + "到队列中,失败。。。");
				}

				// 表示业务逻辑
				Thread.sleep(1000);
			}
		} catch (Exception e) {

		} finally {
			System.out.println("生产者退出线程");
		}
	}
}

/**
 * 消费者线程
 * 
 * @author asong
 */
class ConsumerThread implements Runnable {

	// 和生产者线程操作同一个队列中添加元素
	private BlockingQueue<String> queue;
	private volatile boolean flag = true;

	public ConsumerThread(BlockingQueue<String> queue) {
		this.queue = queue;
	}
	
	@Override
	public void run() {
		try {
			while (flag) {
				System.out.println("消费者,正在从队列中获取数据..");
				String data = queue.poll(2, TimeUnit.SECONDS);
				if (data != null) {
					System.out.println("消费者,拿到队列中的数据data:" + data);
					Thread.sleep(1000);
				} else {
					System.out.println("消费者,超过2秒未获取到数据..");
					//阻塞之后队列中还是没有数据,则停止消费者线程
					flag = false;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			System.out.println("消费者退出线程...");
		}
	}

}

public class ProducerAndConsumer {

	public static void main(String[] args) throws InterruptedException {
		BlockingQueue<String> queue = new LinkedBlockingQueue<String>(10);
		ProducerThread p1 = new ProducerThread(queue);
		ProducerThread p2 = new ProducerThread(queue);
		ConsumerThread c1 = new ConsumerThread(queue);
		
		Thread t1 = new Thread(p1);
		Thread t2 = new Thread(p2);
		Thread t3 = new Thread(c1);
		
		t1.start();
		t2.start();
		t3.start();
		
		// 执行10s 后,停止生产者线程
        Thread.sleep(10 * 1000);
        t1.stop();
        t2.stop();

	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值