`
raymond.chen
  • 浏览: 1437052 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

ArrayBlockingQueue和LinkedBlockingQueue的使用

 
阅读更多

BlockingQueue接口定义了一种阻塞的FIFO queue,每一个BlockingQueue都有一个容量,让容量满时往BlockingQueue中添加数据时会造成阻塞,当容量为空时取元素操作会阻塞。

 

ArrayBlockingQueue是一个由数组支持的有界阻塞队列。在读写操作上都需要锁住整个容器,因此吞吐量与一般的实现是相似的,适合于实现“生产者消费者”模式。

 

基于链表的阻塞队列,同ArrayListBlockingQueue类似,其内部也维持着一个数据缓冲队列(该队列由一个链表构成),当生产者往队列中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回;只有当队列缓冲区达到最大值缓存容量时(LinkedBlockingQueue可以通过构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒,反之对于消费者这端的处理也基于同样的原理。而LinkedBlockingQueue之所以能够高效的处理并发数据,还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。

 

ArrayBlockingQueue和LinkedBlockingQueue的区别:

1. 队列中锁的实现不同

    ArrayBlockingQueue实现的队列中的锁是没有分离的,即生产和消费用的是同一个锁;

    LinkedBlockingQueue实现的队列中的锁是分离的,即生产用的是putLock,消费是takeLock

2. 在生产或消费时操作不同

    ArrayBlockingQueue实现的队列中在生产和消费的时候,是直接将枚举对象插入或移除的;

    LinkedBlockingQueue实现的队列中在生产和消费的时候,需要把枚举对象转换为Node<E>进行插入或移除,会影响性能

3. 队列大小初始化方式不同

    ArrayBlockingQueue实现的队列中必须指定队列的大小;

    LinkedBlockingQueue实现的队列中可以不指定队列的大小,但是默认是Integer.MAX_VALUE

 

offer

将元素插入队列,成功返回true,如果当前没有可用的空间,则返回false

offer(E e, long timeout, TimeUnit unit) 

将元素插入队列,在到达指定的等待时间前等待可用的空间

E poll(long timeout, TimeUnit unit) 

获取并移除队列的头部,在指定的等待时间前等待可用的元素

void put(E e) 

将元素插入队列,将等待可用的空间(堵塞)

take() 

获取并移除队列的头部,在元素变得可用之前一直等待(堵塞)

 

public class BlockingQueueTest {
	private static ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(5, true); //最大容量为5的数组堵塞队列
	//private static LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(5);
	
	private static CountDownLatch producerLatch; //倒计时计数器
	private static CountDownLatch consumerLatch;
	
	public static void main(String[] args) {
		BlockingQueueTest queueTest = new BlockingQueueTest();
		queueTest.test();
	}
	
	private void test(){
		producerLatch = new CountDownLatch(10); //state值为10
		consumerLatch = new CountDownLatch(10); //state值为10
		
		Thread t1 = new Thread(new ProducerTask());
		Thread t2 = new Thread(new ConsumerTask());

		//启动线程
		t1.start();
		t2.start();
		
		try {
			System.out.println("producer waiting...");
			producerLatch.await(); //进入等待状态,直到state值为0,再继续往下执行
			System.out.println("producer end");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		try {
			System.out.println("consumer waiting...");
			consumerLatch.await(); //进入等待状态,直到state值为0,再继续往下执行
			System.out.println("consumer end");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		//结束线程
		t1.interrupt(); 
		t2.interrupt();
		
		System.out.println("end");
	}
	
	//生产者
	class ProducerTask implements Runnable{
		private Random rnd = new Random();
		
		@Override
		public void run() {
			try {
				while(true){
					queue.put(rnd.nextInt(100)); //如果queue容量已满,则当前线程会堵塞,直到有空间再继续
					
					//offer方法为非堵塞的
					//queue.offer(rnd.nextInt(100), 1, TimeUnit.SECONDS); //等待1秒后还不能加入队列则返回失败,放弃加入
					//queue.offer(rnd.nextInt(100));
					
					producerLatch.countDown(); //state值减1
					//TimeUnit.SECONDS.sleep(2); //线程休眠2秒
				}
			} catch (InterruptedException e) {
				//e.printStackTrace();
			}  catch (Exception ex){
				ex.printStackTrace();
			}
		}
	}
	
	//消费者
	class ConsumerTask implements Runnable{
		@Override
		public void run() {
			try {
				while(true){
					Integer value = queue.take(); //如果queue为空,则当前线程会堵塞,直到有新数据加入
					
					//poll方法为非堵塞的
					//Integer value = queue.poll(1, TimeUnit.SECONDS); //等待1秒后还没有数据可取则返回失败,放弃获取
					//Integer value = queue.poll();
					
					System.out.println("value = " + value);
					
					consumerLatch.countDown(); //state值减1
					TimeUnit.SECONDS.sleep(2); //线程休眠2秒
				}
			} catch (InterruptedException e) {
				//e.printStackTrace();
			} catch (Exception ex){
				ex.printStackTrace();
			}
		}
	}
	
}

 

 

分享到:
评论

相关推荐

    并发容器之ArrayBlockingQueue和LinkedBlockingQueue实现原理详解

    ArrayBlockingQueue和LinkedBlockingQueue的主要区别在于存储元素的方式,ArrayBlockingQueue使用数组存储元素,而LinkedBlockingQueue使用链表存储元素。因此,ArrayBlockingQueue更适合固定大小的队列,而...

    并发容器的原理,7大并发容器详解、及使用场景

    它们分别基于链表、数组和优先级堆实现,其中 ArrayBlockingQueue 和 LinkedBlockingQueue 使用 ReentrantLock 和 Condition 实现线程安全,PriorityBlockingQueue 则是一个按优先级排序的队列。 在选择并发容器时...

    java中LinkedBlockingQueue与ArrayBlockingQueue的异同

    Java中的`LinkedBlockingQueue`和`ArrayBlockingQueue`都是`java.util.concurrent`包下的线程安全队列,它们都实现了`BlockingQueue`接口,提供了一种高效、线程安全的数据同步方式。这两种队列在很多方面都有相似之...

    Android开发经验谈:并发编程(线程与线程池)(推荐)

    Android开发经验谈:并发编程(线程与线程池) Android开发中,线程和线程池是两个非常重要的概念。...* ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue等队列的使用方法

    详细分析Java并发集合LinkedBlockingQueue的用法

    LinkedBlockingQueue使用ReentrantLock实现同步锁,用于处理插入队列操作的并发问题,例如put和offer操作。同时,LinkedBlockingQueue也使用Condition实现条件变量,用于队列为空和队列已满的情况下阻塞等待。 四、...

    ArrayBlockingQueue源码分析.docx

    `ArrayBlockingQueue` 内部使用了两个等待条件变量 `notFull` 和 `notEmpty`,它们都是 `ReentrantLock` 的 `Condition` 对象。当队列满时,`put()` 操作会调用 `notFull.await()` 让当前线程等待,而 `take()` 或 ...

    【2018最新最详细】并发多线程教程

    20.并发容器之ArrayBlockingQueue和LinkedBlockingQueue实现原理详解 21.线程池ThreadPoolExecutor实现原理 22.线程池之ScheduledThreadPoolExecutor 23.FutureTask基本操作总结 24.Java中atomic包中的原子操作类...

    LinkedBlockingQueuejava.pdf

    相比于基于数组的`ArrayBlockingQueue`,`LinkedBlockingQueue`在大多数情况下具有更好的性能,因为插入和删除操作不需要数组的重新分配。 五、公平与非公平策略 `LinkedBlockingQueue`有两种策略:公平(FIFO)和...

    ArrayBlockingQueue源码解析-动力节点共

    ArrayBlockingQueue内部由一个数组`items`存储元素,使用两个指针`head`和`tail`分别表示队头和队尾。在并发环境下,为了保证线程安全,ArrayBlockingQueue采用了ReentrantLock(可重入锁)来实现同步,并且在某些...

    java并发之ArrayBlockingQueue详细介绍

    ArrayBlockingQueue使用ReentrantLock和两个Condition来实现线程安全的操作。ReentrantLock是Java并发编程中常用的锁实现,它可以重入锁,允许同一个线程多次获得锁。两个Condition分别是notEmpty和notFull,用于...

    JDK容器学习之Queue:LinkedBlockingQueue

    与数组结构的`ArrayBlockingQueue`不同,`LinkedBlockingQueue`使用单向链表实现,这使得插入和删除操作的效率相对较高,因为它们只需要修改相邻节点的引用。链表的头节点`head`不包含有效数据,而尾节点`last`保存...

    Blocking Queue Usage

    描述中的链接指向了一篇关于阻塞队列使用的博客文章,可能包含了如何在实际应用中有效使用阻塞队列的示例和最佳实践。遗憾的是,由于没有提供实际的博文内容,我们无法进一步讨论其具体内容。 从提供的文件名来看,...

    ThreadPoolExecutor线程池的使用方法

    每种队列都有其特点,ArrayBlockingQueue和LinkedBlockingQueue都是按照先进先出的原则对任务进行排序,而SynchronousQueue不存储元素,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态...

    spring-blockingqueue:用Spring Boot阻止队列

    通常,我们会选择实现BlockingQueue的类,如ArrayBlockingQueue、LinkedBlockingQueue或PriorityBlockingQueue,根据实际需求选择合适的实现。 例如,我们可以创建一个配置类,如下所示: ```java @Configuration ...

    2011.08.30(2)——— java BlockingQueue ExecutorService

    BlockingQueue的一些常见实现包括ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue等,每种实现都有其特定的性能特性和使用场景。例如,ArrayBlockingQueue是基于数组的,提供了固定容量,而...

    简单实现BlockingQueue,BlockingQueue源码详解

    在Java中,有几个实现了BlockingQueue接口的类,如ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue等,它们各有特点: 1. **ArrayBlockingQueue**:基于数组的有界阻塞队列,容量固定,插入和删除...

    并发编程demo测试包含线程池、锁、队列、信号、cas等等

    Java中的ArrayBlockingQueue和LinkedBlockingQueue是两种常见的阻塞队列实现。 信号(信号量/Semaphore)是另一种同步原语,用于限制同时访问特定资源的线程数量。它可以看作是“许可证”,当线程获取一个许可证后...

    java多线程设计模式详解

    BlockingQueue接口及其实现如ArrayBlockingQueue和LinkedBlockingQueue,为线程间的异步数据传递提供便利。 异常处理在多线程编程中也至关重要。当线程中发生异常时,必须正确地捕获和处理,以防止线程突然终止导致...

    [计算机软件及应用]Java高级程序设计实验指导.doc

    4. **线程通信**:研究BlockingQueue接口及其实现,如ArrayBlockingQueue和LinkedBlockingQueue,以及Producer-Consumer模型。 实验结果与分析会包含线程运行的示例,分析并发执行的效果,以及在不同同步策略下的...

Global site tag (gtag.js) - Google Analytics