手撕阻塞队列BlockingQueue

一、什么是阻塞队列

BlockingQueue 继承了 Queue 接口,是队列的一种
当阻塞队列为空时,从队列中获取元素的操作将会被阻塞;当阻塞队列满了,往队列添加元素的操作将会被阻塞。
在多线程中,阻塞的意思是,在某些情况下会挂起线程,一旦条件成熟,被阻塞的线程就会被自动唤醒。

二、BlockQueue常用的方法

在这里插入图片描述
根据插入和取出两种类型的操作,具体分为下面几些类型:
在这里插入图片描述
抛出异常:这时候插入和取出在不能立即被执行的时候就会抛出异常。
特殊值:插入和取出在不能被立即执行的情况下会返回一个特殊的值(true 或者 false)
阻塞:插入和取出操作在不能被立即执行时会阻塞线程,直到条件成熟,被其他线程唤醒
超时:插入和取出操作在不能立即执行的时候会被阻塞一定的时候,如果在指定的时间内没有被执行,那么会返回一个特殊值。
单单从操作的维度来看的话,还是会有点错,因为有些方法是阻塞方法,有些方法不是,我们从阻塞和不阻塞的维度再来一次划分:
在这里插入图片描述

三、常见的阻塞队列

在这里插入图片描述
ArrayBlockingQueueLinkedBlockingQueue是最为常用的阻塞队列,前者使用一个有边界的数组来作为存储介质,而后者使用了一个没有边界的链表来存储数据。

PriorityBlockingQueue是一个优先阻塞队列。所谓优先队列,就是每次从队队列里面获取到的都是队列中优先级最高的,对于优先级,PriorityBlockingQueue需要你为插入其中的元素类型提供一个Comparator,PriorityBlockingQueue使用这个Comparator来确定元素之间的优先级关系。底层的数据结构是堆,也就是我们数据结构中的那个堆。

DelayQueue是一个延时队列,所谓延时队列就是消费线程将会延时一段时间来消费元素。

SynchronousQueue是最为复杂的阻塞队列。SynchronousQueue和前面分析的阻塞队列都不同,因为SynchronousQueue不存在容量的说法,任何插入操作都需要等待其他线程来消费,否则就会阻塞等待。

四、案例说明

SynchronousQueue实现生产者消费者功能,参考: 生产者消费者四种方式实现.

package com.zhang;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

public class BlockingQueueTest {

    public static void main(String[] args) {
        //SynchronousQueue不存在容量的说法,即只存一个元素,任何插入操作都需要等待其他线程来消费,否则就会阻塞等待
        BlockingQueue<String> blockingQueue = new SynchronousQueue<>();
        new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName() + "\t ---> 生产 put 1");
                blockingQueue.put("1");
                System.out.println(Thread.currentThread().getName() + "\t ---> 生产 put 2");
                blockingQueue.put("2");
                System.out.println(Thread.currentThread().getName() + "\t ---> 生产 put 3");
                blockingQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"生产者").start();

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + "\t ---> 消费"+blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + "\t ---> 消费"+blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + "\t ---> 消费"+blockingQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"消费者").start();
    }
}

生产者消费者模型结果
在这里插入图片描述
LinkedBlockingQueue在线程池方面的应用,参考Java 线程池.

package com.test;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.tomcat.util.collections.SynchronizedQueue;

public class ThreadPoolLinkedBlockingQueueTest {

	 public static void main(String[] args) throws Exception {   
	        
		    Runnable runnable = new Runnable() {
				@Override
				public void run() {
						try {
							Thread.sleep(2000);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName()+" run  ");
				}
			};
			/*
	          LinkedBlockingQueue当线程不超过核心线程数之时,会复用核心线程数(可以看出线程名称相同!)
	         */
	    	ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 4, 5, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>());  
	    	executor.execute(runnable);
	    	executor.execute(runnable);
	    	executor.execute(runnable);
	    	System.out.println("先开3个线程-----------");
	    	System.out.println("线程池核心线程数:"+executor.getCorePoolSize());
	    	System.out.println("线程池中线程数:"+executor.getPoolSize());
	    	System.out.println("线程池中队列任务数:"+executor.getQueue().size());
	    	executor.execute(runnable);
	    	executor.execute(runnable);
	    	executor.execute(runnable);
	    	System.out.println("再开3个线程-----------");
	    	System.out.println("线程池核心线程数:"+executor.getCorePoolSize());
	    	System.out.println("线程池中线程数:"+executor.getPoolSize());
	    	System.out.println("线程池中队列任务数:"+executor.getQueue().size());
	    	executor.shutdown();
	    }
}

在这里插入图片描述
LinkedBlockingQueue不受最大线程数影响,但是当其queue有大小限制则会受影响!
在这里插入图片描述
参考文章
https://baijiahao.baidu.com/s?id=1659873724613844132&wfr=spider&for=pc
https://www.cnblogs.com/tjudzj/p/4454490.html
https://blog.csdn.net/xiewenfeng520/article/details/107100303/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值