笔记F《阻塞队列》

8 篇文章 0 订阅

阻塞队列

阻塞队列,首先是一个队列,而一个阻塞队列在数据结构中所起的作用大致如图:
在这里插入图片描述

  • 当阻塞队列是空,从队列中获取元素的操作将会被阻塞;
  • 当阻塞队列为满,往队列中添加元素的操作将会被阻塞。

阻塞,在多线程环境中会挂起线程(线程阻塞),一旦条件满足,被挂起的线程会被自动唤醒。

BlockingQueue

BlockingQueue让我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程:

public class BlockingQueueDemo {
    public static void main(String[] args) throws Exception{
//        List list = null;
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
//        往阻塞队列添加元素
        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));

//        从阻塞队列取元素
        System.out.println(blockingQueue.element());

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

    }
}

继承树:

ArrayBlockingQueue由数组结构组成的有界阻塞队列
LinkedBlockingQueue由链表结构组成的有界阻塞(默认大小Integer.MAX_VALUE())队列
PriorityBlockingQueue支持优先级排序的无界阻塞队列
DelayQueue使用优先队列实现延迟无界阻塞队列
SynchronizedQueue不存储元素的阻塞队列,也即单个元素的队列
LinkedTransferQueue由链表结构组成的无界阻塞队列
LinkedBlockingDeque由链表结构组成的双向阻塞队列
方法类型抛出异常特殊值阻塞超时
插入add(e)offer(e)put(e)offer(e,time,unit)
移除remove()poll()take()poll(time,unit)
检查element()peek()不可用不可用
抛出异常当阻塞队列满时,再往队列里面add插入元素会抛IllegalStateException: Queue full当阻塞队列空时,再往队列Remove元素时候回抛出NoSuchElementException
特殊值插入方法,成功返回true 失败返回false 移除方法,成功返回元素,队列里面没有就返回null
一直阻塞当阻塞队列满时,生产者继续往队列里面put元素,队列会一直阻塞直到put数据or响应中断退出当阻塞队列空时,消费者试图从队列take元素,队列会一直阻塞消费者线程直到队列可用.
超时退出当阻塞队列满时,队列会阻塞生产者线程一定时间,超过后限时后生产者线程就会退出
  • SynchronousQueue 示例
public class SynchronousQueueDemo {
    public static void main(String[] args) {
        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 (Exception e){
                e.printStackTrace();
            }
        },"AAA").start();

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

                try{ TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e){ e.printStackTrace(); }
                System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());

                try{ TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e){ e.printStackTrace(); }
                System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());
            } catch (InterruptedException e){
                e.printStackTrace();
            }
        },"BBB").start();
    }
}

传统版生产消费者模式

public class ProdConsumer_TraditionDemo {
    public static void main(String[] args) {
        ShareData shareData = new ShareData();

        new Thread(()->{
            for(int i = 1;i<=5;i++){
                shareData.increment();
            }
        },"AAA").start();

        new Thread(()->{
            for(int i =1;i<=5;i++){
                shareData.decrement();
            }
        },"BBB").start();
    }
}

class ShareData{
    private int number= 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void increment(){
        lock.lock();
        try{
            while (number != 0){
                condition.await();
            }
            number ++;
            System.out.println(Thread.currentThread().getName()+"\t"+number);
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void decrement(){
        lock.lock();

        try{
            while (number == 0){
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"\t"+number);
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

阻塞版生产消费者模式

public class ProdConsumer_BlockQueueDemo {

    public static void main(String[] args) throws InterruptedException {
        MyResource myResource = new MyResource(new ArrayBlockingQueue<>(10));

        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"\t 生产线程启动");
            try {
                myResource.myProd();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"prod").start();

        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"\t 消费线程启动");
            try {
                myResource.myConsumer();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"consumer").start();

        TimeUnit.SECONDS.sleep(5);

        System.out.println("5秒钟到,main停止");
        myResource.stop();
    }
}

class MyResource{
    private volatile boolean FLAG = true;
    private AtomicInteger atomicInteger = new AtomicInteger();

    BlockingQueue<String> blockingQueue = null;

    public MyResource(BlockingQueue<String> blockingQueue){
        this.blockingQueue = blockingQueue;
        System.out.println(blockingQueue.getClass().getName());
    }

    public void myProd() throws InterruptedException {
        String data = null;
        boolean retValue ;

        while (FLAG){
            data = atomicInteger.incrementAndGet()+"" ;
            retValue = blockingQueue.offer(data,2L, TimeUnit.SECONDS);

            if(retValue){
                System.out.println(Thread.currentThread().getName()+"\t插入队列 "+data+" 成功");
            }else{
                System.out.println(Thread.currentThread().getName()+"\t插入队列 "+data+" 失败");
            }

            TimeUnit.SECONDS.sleep(1);
        }
        System.out.println(Thread.currentThread().getName()+"\t生产停止");
    }

    public void myConsumer() throws InterruptedException {
        String result = null;
        while (FLAG){
            result = blockingQueue.poll(2L,TimeUnit.SECONDS);

            if(null == result || result.equalsIgnoreCase("")){
                FLAG = false;
                System.out.println(Thread.currentThread().getName()+"\t 超过2秒,消费退出");
                return;
            }

            System.out.println(Thread.currentThread().getName()+"\t消费队列 "+result+" 成功");
        }
    }

    public void stop(){
        this.FLAG = false;
    }
}

synchronized & lock

synchronized和lock的区别:

synchronizedlock
原始构成synchronized属于JVM层面,底层通过 monitorenter 和 monitorexit 两个指令实现lock是JUC提供的具体类,是API层面的东西
用法synchronized 不需要用户手动释放锁,当 synchronized 代码执行完毕之后会自动让线程释放持有的锁lock 需要一般使用try-finally模式去手动释放锁,并且加锁-解锁数量需要一直,否则容易出现死锁或者程序不终止现象
等待是否可中断synchronized是不可中断的,除非抛出异常或者程序正常退出lock可中断: 1. 设置超时方法tryLock(time, unit);2. 使用lockInterruptibly,调用iterrupt方法可中断;
是否公平锁synchronized是非公平锁lock默认是非公平锁,但是可以通过构造函数传入boolean类型值更改是否为公平锁
锁是否能绑定多个条件(condition)synchronized没有condition的说法,要么唤醒所有线程,要么随机唤醒一个线程lock可以使用condition实现分组唤醒需要唤醒的线程,实现精准唤醒
/*
 * 题目:多线程之间按顺序调用,实现A->B->C三个线程启动,要求如下:
 * A打印5次,B打印10次,C打印15次
 * 紧接着
 * A打印5次,B打印10次,C打印15次
 * 。。。。。
 * 打印10轮
 *
 * */
public class SyncAndReentrantLockDemo {
    public static void main(String[] args) {
        ShareResource shareResource = new ShareResource();
        new Thread(()->{
            for(int i =1;i<=10;i++){
                shareResource.printA();
            }
        },"A").start();

        new Thread(()->{
            for(int i = 1;i<=10;i++){
                shareResource.printB();
            }
        },"B").start();

        new Thread(()->{
            for(int i =1;i<=10;i++){
                shareResource.printC();
            }
        },"C").start();
    }
}

class ShareResource{
    private int number = 1;
    private Lock lock = new ReentrantLock();
    private Condition cond1 =  lock.newCondition();
    private Condition cond2 = lock.newCondition();
    private Condition cond3 = lock.newCondition();

    public void printA(){
        lock.lock();
        try{
            while (number != 1){
                cond1.await();
            }

            for(int i =1;i<=5;i++){
                System.out.println(Thread.currentThread().getName()+"\t"+i);
            }

            number = 2;
            cond2.signal();

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void printB(){
        lock.lock();

        try{
            while (number != 2){
                cond2.await();
            }

            for(int i = 1;i<=10;i++){
                System.out.println(Thread.currentThread().getName()+"\t"+i);
            }
            number = 3;
            cond3.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void printC(){
        lock.lock();
        try{
            while (number != 3){
                cond3.await();
            }

            number = 1;
            cond1.signal();

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值