阻塞队列(BlockingQueue)

阻塞队列

1.什么是阻塞队列?

  • 阻塞队列首先是一个队列,当队列是的时候,从队列获取元素的操作将会被阻塞,当队列是的时候,从队列插入元素的操作将会被阻塞。
    在这里插入图片描述
        线程1往队阻塞列里添加元素,线程2从阻塞队列中移除元素。
        试图从空队列里移除元素的线程2(包括其他线程)会被阻塞,直到线程1(或其他线程)往队列里新增元素。
        试图往已满的阻塞队列里新增元素的线程1(包括其他线程)会被阻塞,直到线程2(或其他线程)移除一个(或多个)是阻塞队列变得空闲才能继续新增元素。
        至于何时挂起线程和唤醒线程,我们不需要关心,BlockingQueue帮我们做好了。

2.阻塞队列分类(加粗的为常用)

(1)ArrayBlockingQueue:由数组结构组成的有界阻塞队列。(此队列按照先进先出(FIFO)的原则对元素进行排序。支持公平锁和非公平锁。默认是非公平)

在这里插入图片描述

(2)LinkedBlockingQueue:由链表结果组成的有界阻塞队列(默认大小Integer.MAX_VALUE)阻塞队列。按照先进先出的顺序进行排序。
在这里插入图片描述

(3)SychronousQueue:不存储元素的阻塞队列,也即单个元素队列。(默认是非公平锁)
在这里插入图片描述

(4)PriorityBlockingQueue:支持优先级排序的无界阻塞队列,默认情况下元素采用自然顺序升序排列。也可以自定义类实现compareTo()方法来指定元素排序规则(默认的容量是11,数据结构是最小二叉堆)。

(5)DelayQueue:使用优先级队列实现的延迟无界阻塞队列。用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,即队头对象的延迟到期时间最长。注意:不能将null元素放置到这种队列中。

(6)LinkedTransferQueue:由链表结构组成的无界阻塞队列。

(7)LinkedBlockingDeque:由链表结构组成的双端阻塞队列。即可以从队列的两端插入和移除元素。(只有这个没有锁

3.BlockingQueue的核心方法

方法\处理方式抛出异常返回特殊值一直阻塞超时退出
插入方法add(e)offer(e)put(e)offer(e,time,unit)
移除方法remove()poll()take()poll(time,unit)
检查方法element()peek()不可用不可用

3.1抛出异常(指在阻塞对列满时,继续add元素会抛出 java.lang.IllegalStateException异常,对列为空时,继续remove元素会抛java.util.NoSuchElementException异常,element()检查对列,并返回队首元素,如果是是空队列则抛出java.util.NoSuchElementException异常)
以ArrayBlockingQueue为例:

package com.sk.Multithreading;

import java.util.concurrent.*;

public class BlockingQueueDemo {
    public static void main(String[] args) throws InterruptedException {

        BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(blockingQueue.add(1));
        System.out.println(blockingQueue.add(1));
        System.out.println(blockingQueue.add(1));
        System.out.println(blockingQueue.add(1));


    }
}

在这里插入图片描述

package com.sk.Multithreading;

import java.util.concurrent.*;

public class BlockingQueueDemo {
    public static void main(String[] args) throws InterruptedException {

        BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(blockingQueue.add(1));
        System.out.println(blockingQueue.add(1));
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());


    }
}

在这里插入图片描述
add方法底层是去调用offer方法,根据offer方法的返回值(true,false)来决定是否抛出异常
在这里插入图片描述
remove方法底层调用poll方法,根据返回值是未null,来决定是否抛出异常
在这里插入图片描述
element()方法底层是peek()方法,也是根据返回值是否为null,来判定是否抛出异常
在这里插入图片描述

3.2返回特殊值(插入成功返回true失败返回false,队列为空时,移除元素返回null)
以ArrayBlockingQueue为例

package com.sk.Multithreading;

import java.util.concurrent.*;

public class BlockingQueueDemo {
    public static void main(String[] args) throws InterruptedException {

        BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(blockingQueue.offer(1));
        System.out.println(blockingQueue.offer(1));
        System.out.println(blockingQueue.offer(1));
        System.out.println(blockingQueue.offer(1));
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());


    }
}

运行结果
在这里插入图片描述
3.3一直阻塞(当队列满时,继续往队列put元素的线程会被阻塞,直到能put进去;当队列为空时,从队列取元素的线程会被阻塞,直到队列不为空,方法没有返回值)

package com.sk.Multithreading;

import java.util.concurrent.*;

public class BlockingQueueDemo {
    public static void main(String[] args) throws InterruptedException {

        BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(3);

        blockingQueue.put(1);
        blockingQueue.put(1);
        blockingQueue.put(1);
        System.out.println("醉后不知天在水,满船清梦压星河。");
        blockingQueue.put(1);


    }
}

在这里插入图片描述
可以看出打印出诗句后,程序并没有结束,这是因为线程被挂起,还有一个元素没有put进去。
在这里插入图片描述

3.4超时(设置一定时间,超出这个时间,线程会退出)

package com.sk.Multithreading;

import java.util.concurrent.*;

public class BlockingQueueDemo {
    public static void main(String[] args) throws InterruptedException {

        BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(3);

        blockingQueue.offer(1,2,TimeUnit.SECONDS);
        blockingQueue.offer(1,2,TimeUnit.SECONDS);
        blockingQueue.offer(1,2,TimeUnit.SECONDS);
        System.out.println("醉后不知天在水,满船清梦压星河。");
        blockingQueue.offer(1,2,TimeUnit.SECONDS);


    }
}

结果(此时还未超出2秒的时间,程序未结束)
在这里插入图片描述
2秒后程序结束
在这里插入图片描述
3.4SynchronousQueue不存储元素,生产一个消费一个。

package com.sk.Multithreading;

import java.util.concurrent.*;

public class BlockingQueueDemo {
    public static void main(String[] args) {

        BlockingQueue<Integer> blockingQueue = new SynchronousQueue<Integer>();
        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(2);
                System.out.println(Thread.currentThread().getName() + "\t take " + blockingQueue.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + "\t take " + blockingQueue.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + "\t take " + blockingQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "花辞树").start();

    }
}

在这里插入图片描述
让花辞树线程线程睡眠2秒是方面观察,当队列中put进去一个元素,只有take把元素拿出来后,下一个元素才能put进去。

4.阻塞队列的应用

4.1生产者和消费者模式

package com.sk.Multithreading;

import java.util.concurrent.*;

class DataSource{
    BlockingQueue<Integer> queue ;
    public DataSource(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }
}

public class BlockingQueueDemo {
    public static void main(String[] args) {
        BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(5);
        DataSource dataSource = new DataSource(blockingQueue);
        new Thread(() -> {
            try {
                for (int i = 1; i <= 10 ; i++) {
                    dataSource.queue.put(i);
                    System.out.println(Thread.currentThread().getName()+i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "玉无双").start();//生产者

        new Thread(() -> {
            try {
                for (int i = 1; i <= 10; i++) {
                    System.out.println(Thread.currentThread().getName()+dataSource.queue.take());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "花辞树").start();//消费者

    }
}

4.2.消息中间件底层核心就是阻塞队列
4.3线程池

  • SingleThreadExecutor和·FixedThreadPool用的是LinkedBlockingQueue
  • newCachedThreadPool用的是SynchronousQueue
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值