多线程(二)阻塞队列

版权声明:转载请说明出处 https://blog.csdn.net/qq_41816123/article/details/89316393

先分享个有趣的故事给大家,是女大学生被男老师追求的事情,故事的结局不是你们想的那种老牛吃嫩草,吃不到就用强的那种,男的40,女的19,我个人感觉男的比较虚伪的认真了,这是我的个人看法,也可以说他爱的太深了,这个反正我是不太信,女的有点优柔寡断,明明不喜欢说出来就好,以怕挂科为理由,一直和老师瞎扯正义和大道理,现在的老师还不至于无缘无故就挂你科。故事到这了,不说了,反正恋爱的请珍惜,分手的祝你分手快乐祝你找个更好的,单身的对自己好点(爱护自己的双手)。


目录

  1. 常用阻塞场景
  2. 定义
  3. 核心方法
  4. java中阻塞队列种类
  5. 实现原理(源码查看)
  6. 使用场景
  7. 总结

1.常用的阻塞场景
  1. 队列中的生产者和消费者
    生产者:是往队列里添加元素的线程
    消费者:是从队列里拿元素的线程
    阻塞队列就是容器,存放元素(别蒙蔽好吧,元素就是object)
  2. 阻塞场景
    1)当队列中没有数据的情况下,消费者端的所有线程被挂起(也就是阻塞了),直到有数据存放入队列
    2)当队列中填满数据的情况下,生产者端的所有线程被挂起,直到队列中有空的位置,线程被自动唤醒
2.定义

符合1.2中两种场景的队列,称为阻塞队列

3.核心方法

放入数据的方法:

  • offer(anObject):表示将anObject放入到队列BlockingQueue中,BlockingQueue中可以容纳就返回true,否则返回false。(本方法是不会阻塞当前执行方法的线程,反回false,有了解吗?)
  • offer(E o,long timeout,TimeUnit unit):可以设定等待时间。如果在指定时间内还不能往队列中加入BlockingQuere,则返回false。
  • put(anObjiect):和offer是一样的,但是失败它不会返回false,会直接阻塞当前方法的线程。

获取数据的方法:

  • poll(long timeout,TimeUnit unit):从BlockingQueue中取出队首的对象。在指定时间内,队列一旦有数据可取,则立即返回队列中的数据,否则超过时间就返回失败。
  • take():取走BlockingQueue里排在首位的对象。若BlockingQueue为空,则阻断进入等待状态,直到BlockingQueue有新的数据被加入。
  • drainTo():一次性从BlockingQueue获取所有可用的数据对象(还可以指定获取数据的个数)。通过该方法,可以提升获取数据的效率;无需多次分批加锁或者释放锁。

当然不单单就这6个方法,你可以点看一下,我就不一一介绍了。
在这里插入图片描述

4.java中阻塞队列种类
  • ArrayBlockingQueue:由数组结构组成的有界阻塞队列
  • LinkedBlockingQueue:由链表结构组成的有界组赛队列
  • PriorityBlockingQueue:支持优先级排序的无界组赛队列
  • DelayQueue:使用优先级排序的无界阻塞队列
  • SynchronousQueue:不存储元素的组赛队列
  • LinkedTransferQueue:由链表结构组成的无界组赛队列
  • LinkedBlockingDeque:由链表结构组成的双向阻塞队列

特点:
1.通常情况下,用前两种就足够了。

2.PriorityBllockingQueue不能保证同优先级元素的顺序。

3.DelayQueue指定元素到期的时间,只有在元素到期时才能从队列中取走。

4.SynchronousQueue严格来说它并不是容器。由于队列没有容量,因此不能调用peek操作(返回队列头元素)

5.LinkedTransferQueue实现了一个重要接口TransferQueue该接口有5个方法,其中有3个比较重要的方法,一个是transfer(E e)如果没有消费者等待接受数据,会把元素插入到队列尾部,队列进入阻塞状态,直到有消费者取走该元素,tryTransfer(E e)看名字就知道不会阻塞线程,当然也可以给它传入时间,在指定的时间内元素未被消费者获取则返回false。

6.LinkedBlockingDeque是双向的队列,多线程同时入队时减少了一半的竞争。其中的First单词结尾的方法表示插入、获取或者移除双端队列的第一个元素;以Last单纯结尾的方法,表示插入、获取或者移除双端队列的最后一个元素。

扫描二维码关注公众号,回复: 5987208 查看本文章
5.实现原理(源码查看)

老规矩,打开我们的Source Insight 4.0,我以ArrayBlockingQueue为例,搜索看一下这个类定义了那些变量
在这里插入图片描述
我们维护的是一个Object的数组,takeIndex和putIndex分别表示队首元素和队尾元素的下标,count表示队列元素的个数,lock则是一个可重入锁,notEmpty和notFull是等待条件。在看看关键方法put
在这里插入图片描述
可以看出,先获取锁,在判断当前元素个数是否等于数组长度,如果相等,则调用notFull.await()进行等待。当此线程被其他线程唤醒时,通过enqueue(e)方法插入元素,最后解锁。在看看enqueue(e)方法
在这里插入图片描述
插入成功后,就通过notEmpty.signal()唤醒正在等待取元素的线程。再来看看take方法。
在这里插入图片描述
这个应该差不多懂了吧,和put方法类似,如果可以取,就通过dequeue方法取得元素,我们在看看dequeue方法得源码。
在这里插入图片描述
和enqueue方法类似,在获取元素后,通过等待条件notFull的signal方法唤醒正在等待插入元素的线程。

6.使用场景

第一种使用Object.wait()、Object.notify()和非阻塞队列实现生产者-消费者模式

package com.example.syt.myapplication;

import java.util.PriorityQueue;

/**
 * Created by syt on 2019/4/15.
 */

public class Test {
    private int queueSize=10;
    private PriorityQueue<Integer> queue=new PriorityQueue<Integer>(queueSize);

    public static void main(String[] args) {
        Test test=new Test();
        Producer producer=test.new Producer();
        Consumer consumer=test.new Consumer();
        producer.start();
        consumer.start();
    }
    class Consumer extends Thread{
        @Override
        public void run() {
            while (true){
                synchronized (queue){
                    while (queue.size()==0){
                        try{
                            System.out.println("队列空,等待数据");
                            queue.wait();
                        }catch (InterruptedException e){
                            e.printStackTrace();
                            queue.notify();
                        }
                    }
                    //每次移走队首元素
                    queue.poll();
                    queue.notify();
                }
            }
        }
    }
    class Producer extends Thread{
        @Override
        public void run() {
            while (true){
                synchronized (queue){
                    while (queue.size()==queueSize){
                        System.out.println("队列满,等待有余空间");
                        try {
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notify();
                        }
                    }
                    //每次插入一个元素
                    queue.offer(1);
                    queue.notify();
                }
            }
        }
    }
}

在看看我们使用阻塞队列去实现生产者-消费者模式

package com.example.syt.myapplication;

import java.util.concurrent.LinkedBlockingDeque;

/**
 * Created by syt on 2019/4/15.
 */

public class Test {
    private int queueSize=10;
    private LinkedBlockingDeque<Integer> queue=new LinkedBlockingDeque<Integer>(queueSize);

    public static void main(String[] args) {
        Test test=new Test();
        Producer producer=test.new Producer();
        Consumer consumer=test.new Consumer();
        producer.start();
        consumer.start();

    }
    class Consumer extends Thread{
        @Override
        public void run() {
            while (true){
                try {
                    queue.take();
                    System.out.println("取队首");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    class Producer extends Thread{
        @Override
        public void run() {
            try {
                queue.put(1);
                System.out.println("往队列加一个");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

总结

阻塞队列的特点:队空时消费阻塞,队满时生产阻塞。多线程消费数据起到了加速消费的作用,使得生产的数据不会在队里积压过多,而生产的数据也不会丢失处理了。

奇淫巧计,为求一赞,如有疑问或者博主写的不足的地方请留言或者联系QQ2714730493

猜你喜欢

转载自blog.csdn.net/qq_41816123/article/details/89316393