【并发:Java中的阻塞队列】简单理解

博文部分参考:【Java 编程思想 第四版】+ 电子书总结

阻塞队列

1. 什么是阻塞队列?

  • 阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。
    在这里插入图片描述

  • 阻塞队列提供了四种处理方法:
    在这里插入图片描述
    1.抛出异常:是指当阻塞队列满时候,再往队列里插入元素,会抛出IllegalStateException(“Queue full”)异常。当队列为空时,从队列里获取元素时会抛出NoSuchElementException异常 。
    2.返回特殊值:插入方法会返回是否成功,成功则返回true。移除方法,则是从队列里拿出一个元素,如果没有则返回null
    3.一直阻塞:当阻塞队列满时,如果生产者线程往队列里put元素,队列会一直阻塞生产者线程,直到拿到数据,或者响应中断退出。当队列空时,消费者线程试图从队列里take元素,队列也会阻塞消费者线程,直到队列可用。
    4.超时退出:当阻塞队列满时,队列会阻塞生产者线程一段时间,如果超过一定的时间,生产者线程就会退出。

2. Java里的阻塞队列

自从Java 1.5之后,在java.util.concurrent包下提供了若干个阻塞队列,主要有以下几个:

  • 1、ArrayBlockingQueue: 基于数组实现的一个阻塞队列,在创建ArrayBlockingQueue对象时必须制定容量大小。并且可以指定公平性与非公平性,默认情况下为非公平的,即不保证等待时间最长的队列最优先能够访问队列。
  • 2、LinkedBlockingQueue:基于链表实现的一个阻塞队列,在创建LinkedBlockingQueue对象时如果不指定容量大小,则默认大小为Integer.MAX_VALUE。
  • 3、PriorityBlockingQueue:以上2种队列都是先进先出队列,而PriorityBlockingQueue却不是,它会按照元素的优先级对元素进行排序,按照优先级顺序出队,每次出队的元素都是优先级最高的元素。注意,此阻塞队列为无界阻塞队列,即容量没有上限(通过源码就可以知道,它没有容器满的信号标志),前面2种都是有界队列。
  • 4、DelayQueue: 基于PriorityQueue,一种延时阻塞队列,DelayQueue中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue也是一个无界队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞。

3.栗子一: 下面使用ArrayBlockQueue来实现的生产者消/费者模式

  • 我们先来大概了解下什么是生产者消费者模型:生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中添加产品,消费者从存储空间中取走产品,当存储空间为空时,消费者阻塞,当存储空间满时,生产者阻塞
    在这里插入图片描述
public class BlockQueue {
    
    
    public static void main(String[] args) 
    {
    
      
        BigPlate plate = new BigPlate();  

        for(int i = 0; i < 10; i++) // 先启动10个放鸡蛋线程 
        {
    
      
            new Thread(new BigPlate.AddThread(plate)).start();  
        }  

        for(int i = 0; i < 10; i++) // 再启动10个取鸡蛋线程 
        {
    
      
            new Thread(new BigPlate.GetThread(plate)).start();  
        }  
    } 
}

/** 定义一个盘子类,可以放鸡蛋和取鸡蛋 */  
class BigPlate 
{
    
      
    /** 装鸡蛋的盘子,大小为5 */  
    private BlockingQueue<Object> eggs = new ArrayBlockingQueue<>(5);  

    /** 放鸡蛋 */  
    public void putEgg(Object egg) 
    {
    
      
        try 
        {
    
      
            eggs.put(egg);// 向盘子末尾放一个鸡蛋,如果盘子满了,当前线程阻塞 

        } catch (InterruptedException e) 
        {
    
      
            e.printStackTrace();  
        }  
        // 下面输出有时不准确,因为与put操作不是一个原子操作 
        System.out.println("放入鸡蛋");  
    }  

    /** 取鸡蛋 */  
    public Object getEgg() 
    {
    
      
        Object egg = null;  
        try 
        {
    
      
            egg = eggs.take();// 从盘子开始取一个鸡蛋,如果盘子空了,当前线程阻塞 
        } catch (InterruptedException e) 
        {
    
      
            e.printStackTrace();  
        }  
        // 下面输出有时不准确,因为与take操作不是一个原子操作 
        System.out.println("拿到鸡蛋");  
        return egg;  
    }  

    /** 放鸡蛋线程 */  
    static class AddThread extends Thread 
    {
    
      
        private BigPlate plate;  
        private Object egg = new Object();  

        public AddThread(BigPlate plate) 
        {
    
      
            this.plate = plate;  
        }  

        public void run() 
        {
    
      
            plate.putEgg(egg);  
        }  
    }  

    /** 取鸡蛋线程 */  
    static class GetThread extends Thread 
    {
    
      
        private BigPlate plate;  

        public GetThread(BigPlate plate) 
        {
    
      
            this.plate = plate;  
        }  

        public void run() 
        {
    
      
            plate.getEgg();  
        }  
    }

栗子二: 要求不用阻塞队列,使用 LinkedList结合多线程知识实现生产者消/费者模式

import java.util.LinkedList;
import java.util.concurrent.TimeUnit;

class BlockingQueue<E>{
    
    
    private final LinkedList<E> queue = new LinkedList<>();
    private static int max;//表示堵塞队列存储的最大值
    private static final int DEFAULT_MAX_VALUE = 10;
    public BlockingQueue(){
    
    
        this(DEFAULT_MAX_VALUE);
    }

    public BlockingQueue(int max) {
    
    
        this.max = max;
    }

    //生产数据
    public void put(E value){
    
    
        synchronized (queue){
    
    
            if (queue.size()>=max){
    
    
                System.out.println(Thread.currentThread().getName()+":is full");
                try {
    
    
                    //没有位置,当前生产数据就会等待
                    queue.wait();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+"添加"+value);
            queue.addLast(value);
            queue.notifyAll();
        }
    }

    //消费数据
    public E take(){
    
    
        synchronized (queue){
    
    
            //判断是否有可消费的数据
            if (queue.isEmpty()){
    
    
                System.out.println(Thread.currentThread().getName()+":isEmpty");
                try {
    
    
                    queue.wait();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
            E result = queue.removeFirst();
            System.out.println("删除"+result);
            queue.notifyAll();
            return result;
        }
    }
}
public class TestDemo13 {
    
    
    public static void main(String[] args) {
    
    
        BlockingQueue<Integer> queue = new BlockingQueue<>();
        new Thread("product"){
    
    
            @Override
            public void run() {
    
    
                while (true){
    
    
                    queue.put((int)(Math.random()*1000));
                }
            }
        }.start();
        new Thread("Consumer"){
    
    
            @Override
            public void run() {
    
    
                while (true){
    
    
                    queue.take();
                    try {
    
    
                        TimeUnit.MILLISECONDS.sleep(1);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }
}

猜你喜欢

转载自blog.csdn.net/qq_44682003/article/details/111463697
今日推荐