并发编程之BlockingQueue

一、概述

1. BlockqingQueue是阻塞式队列中的顶级接口
2. 不同于以往的队列,BlockingQueue一般是有界限的
3. 在队列为空的时候进行获取操作会产生阻塞
4. 在队列已满的情况下继续存储元素会产生阻塞
5. 遵循先进先出(FIFO)的原则
6. 适用于生产消费模型

二、重要方法

              抛出异常	返回特殊值	阻塞           定时阻塞

队列已满时,添加 add(o) offer(o) put(o) offer(o, time, unit)
队列为空时,获取 remove() poll() take() poll(time, unit)

三、ArrayBlockingQueue - 阻塞式顺序队列

1. 在使用的时候需要指定容量/界限
2. 底层基于数组来存储数据
3. 容量在指定之后不能改动
4. 遵循先进先出(FIFO)的原则

四、LinkedBlockingQueue - 阻塞式链式队列

1. 在使用的时候可以指定容量也可以不指定容量
2. 如果不指定容量,则容量默认为Integer.MAX_VALUE,此时认为容量是无限的
3. 如果指定容量,则容量指定之后不可变
4. 底层基于节点(链表)来存储数据
5. 遵循先进先出(FIFO)的原则

五、PriorityBlockingQueue - 阻塞式优先级队列

概述
1. 在使用的时候可以指定容量也可以不指定
2. 如果不指定容量,则容量默认为11
3. 如果指定容量,则容量指定之后不可改变
4. 在指定容量的时候,最大不能超过Integer.MAX_VALUE-8
5. PriorityBlockingQueue要求存储的元素对应的类必须实现Comparable接口,重写其中的compareTo方法来指定比较规则
6. PriorityBlockingQueue在存储元素的时候会根据指定的比较规则对元素进行排序
7. PriorityBlockingQueue在迭代的时候不保证元素的排序顺序
示例

public class PriorityBlockingQueueDemo {
 
    public static void main(String[] args) throws Exception {
 
        // 创建队列
        PriorityBlockingQueue<Student> queue = new PriorityBlockingQueue<>();
 
        // 添加元素
        queue.put(new Student("Amy", 16));
        queue.put(new Student("Bob", 25));
        queue.put(new Student("Cathy", 20));
        queue.put(new Student("David", 13));
 
        // 遍历队列
        // 需要注意的是,如果想要拿到排序的结果,不能以迭代的方法获取
        for (int i = 0; i < 4; i++) {
            System.out.println(queue.take());
        }
 
    }
 
}
 
class Student implements Comparable<Student> {
 
    private String name;
    private int age;
 
    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
 
    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }
 
    // 在这个方法中指定比较规则
    // 根据学生的年龄进行升序排序
    @Override
    public int compareTo(Student o) {
        return this.age - o.age;
    }
 
}

六、SynchronousQueue - 同步队列

1. 容量默认为1,并且只能为1,因此只能存储1个元素
2. 如果该队列已有一个元素,则试图向队列中新添一个新元素的线程将会阻塞,直到另一个线程将该元素从队列中抽走
3. 如果该队列为空,则试图从队列中抽取一个元素的线程将会阻塞,直到另一个线程向队列中添加了一个新的元素
4. 一般会将同步队列称之为是数据的汇合点

七、BlockingDeque - 阻塞式双向队列

1. BlockingDeque继承了BlockingQueue
2. 在使用的时候,也是需要指定容量的
3. 该队列称之为双向队列,即队列的两端都可以添加元素,也可以从队列的两端获取元素

八、BlockingQueue中实现入队和出队的操作

  1. 使用object中的wait和notify来实现阻塞队列
public class Test {

    //模拟生产和消费的对象
    static class Buffer {
        private int maxSize;
        private List<Date> storage;

        Buffer(int size) {
            maxSize = size;
            storage = new LinkedList<>();
        }

        //生产方法
        public synchronized void put() {
            try {
                while (storage.size() == maxSize) {//如果队列满了
                    System.out.print(Thread.currentThread().getName() + ": wait \n");
                    wait();//阻塞线程
                }
                storage.add(new Date());
                System.out.print(Thread.currentThread().getName() + ": put:" + storage.size() + "\n");
                Thread.sleep(1000);
                notifyAll();//唤起线程
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        //消费方法
        public synchronized void take() {
            try {
                while (storage.size() == 0) {//如果队列空了
                    System.out.print(Thread.currentThread().getName() + ": wait \n");
                    wait();//阻塞线程
                }
                Date d = ((LinkedList<Date>) storage).poll();
                System.out.print(Thread.currentThread().getName() + ": take:" + storage.size() + "\n");
                Thread.sleep(1000);
                notifyAll();//唤起线程
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    //生产者
    static class Producer implements Runnable {
        private Buffer buffer;

        Producer(Buffer b) {
            buffer = b;
        }

        @Override
        public void run() {
            while (true) {
                buffer.put();
            }
        }
    }

    //消费者
    static class Consumer implements Runnable {
        private Buffer buffer;

        Consumer(Buffer b) {
            buffer = b;
        }

        @Override
        public void run() {
            while (true) {
                buffer.take();
            }
        }
    }

    //
    public static void main(String[] arg) throws InterruptedException {
        Buffer buffer = new Buffer(5);
        Producer producer = new Producer(buffer);
        Consumer consumer = new Consumer(buffer);
        //创建线程执行生产和消费
        for (int i = 0; i < 3; i++) {
            new Thread(producer, "producer-" + i).start();
        }
        //可以清楚地看到队满阻塞
        Thread.sleep(10000L);
        for (int i = 0; i < 3; i++) {
            new Thread(consumer, "consumer-" + i).start();
        }
    }


}
  1. BlockingQueue中的Lock与Condition实现阻塞队列的入队与出队
public class Test2 {

    static class Buffer {
        private final Lock lock;
        private final Condition notFull;
        private final Condition notEmpty;
        private int maxSize;
        private List<Date> storage;

        Buffer(int size) {
            //使用锁lock,并且创建两个condition,相当于两个阻塞队列
            lock = new ReentrantLock();
            notFull = lock.newCondition();
            notEmpty = lock.newCondition();
            maxSize = size;
            storage = new LinkedList<>();
        }

        public void put() {
            lock.lock();
            try {
                while (storage.size() == maxSize) {//如果队列满了
                    System.out.print(Thread.currentThread().getName() + ": wait \n");
                    notFull.await();//阻塞生产线程
                }
                storage.add(new Date());
                System.out.print(Thread.currentThread().getName() + ": put:" + storage.size() + "\n");
                Thread.sleep(1000);
                notEmpty.signalAll();//唤醒消费线程
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        public void take() {
            lock.lock();
            try {
                while (storage.size() == 0) {//如果队列满了
                    System.out.print(Thread.currentThread().getName() + ": wait \n");
                    notEmpty.await();//阻塞消费线程
                }
                Date d = ((LinkedList<Date>) storage).poll();
                System.out.print(Thread.currentThread().getName() + ": take:" + storage.size() + "\n");
                Thread.sleep(1000);
                notFull.signalAll();//唤醒生产线程

            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    static class Producer implements Runnable {
        private Buffer buffer;

        Producer(Buffer b) {
            buffer = b;
        }

        @Override
        public void run() {
            while (true) {
                buffer.put();
            }
        }
    }

    static class Consumer implements Runnable {
        private Buffer buffer;

        Consumer(Buffer b) {
            buffer = b;
        }

        @Override
        public void run() {
            while (true) {
                buffer.take();
            }
        }
    }

    public static void main(String[] arg) {
        Buffer buffer = new Buffer(10);
        Producer producer = new Producer(buffer);
        Consumer consumer = new Consumer(buffer);
        for (int i = 0; i < 3; i++) {
            new Thread(producer, "producer-" + i).start();
        }
        for (int i = 0; i < 3; i++) {
            new Thread(consumer, "consumer-" + i).start();
        }
    }


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值