(多线程)阻塞队列

目录

标准库中的阻塞队列

实现阻塞队列

普通队列

代码

线程安全问题

修改后的代码

实现阻塞功能

代码


标准库中的阻塞队列

       标准库中为我们提供了BlockingQueue这个类,我们可以调用这个类中的put,take方法来分别插入元素和获取元素。和普通队列相同的是都是遵循先进先出的原则,不同的是阻塞队列多了阻塞的功能,当队列为空的时候我们继续take取元素就会阻塞,同样当队列满的时候我们继续put也会阻塞。

 

        可以看出我们进行了两次take,而队列里只有一个元素,所以打印了一个“1”之后,线程就阻塞了。 

实现阻塞队列

普通队列

       我们自己实现带有阻塞功能的队列,首先我们先实现一个普通的队列。(这里主要实现put和take两个主要的方法)

       我们采用循环数组的方式来实现队列。head表示队列的头下标,tail表示队列的为下标。循环队列,我们当然要使下标到达数组长度的时候循环下标。这里直接将下标置为0即可。

代码

public class MyBlockingQueue {
    //采用循环队列的方式来实现阻塞队列
    private int[] elem = new int[1000];
    private int head;//头
    private int tail;//尾
    private int size;//更新当前队列中的有效数据的个数

    //put方法,来往队列里存数据
    public void put(int numb){
        if(size == elem.length){
          //队列满了
            System.out.println("队列已满");
            return;
        }
      //新数据添加到tail位置
        elem[tail] = numb;
        tail++;
        if(tail == elem.length){
            //让下标进行循环
            tail = 0;
        }
        size++;
    }

    //take方法,从队列中取出元素
    public int take(){
        if(size == 0){
            //当前队列中没有元素
          return -1;
        }
        //取出head下标的元素
        int ret = elem[head];
        head++;
        if(head == elem.length){
            //同理,循环下标
            head = 0;
        }
        size--;
        return ret;
    }
}

线程安全问题

在多线程环境下,当前的代码是有线程安全问题的,这里回顾一下线程安全问题的原因

       对应我们目前的代码,head,tail,size,elem数组,这些变量都存在多个线程修改的问题。所以这里简单处理,可使用volatile来修饰变量,给put和take方法上加锁。 

修改后的代码

public class MyBlockingQueue {
    //采用循环队列的方式来实现阻塞队列
    volatile private int[] elem = new int[1000];
    volatile private int head;//头
    volatile private int tail;//尾
    volatile private int size;//更新当前队列中的有效数据的个数

    //put方法,来往队列里存数据
    synchronized public void put(int numb){
        if(size == elem.length){
          //队列满了
            System.out.println("队列已满");
            return;
        }
      //新数据添加到tail位置
        elem[tail] = numb;
        tail++;
        if(tail == elem.length){
            //让下标进行循环
            tail = 0;
        }
        size++;
    }

    //take方法,从队列中取出元素
    synchronized public int take(){
        if(size == 0){
            //当前队列中没有元素
          return -1;
        }
        //取出head下标的元素
        int ret = elem[head];
        head++;
        if(head == elem.length){
            //同理,循环下标
            head = 0;
        }
        size--;
        return ret;
    }
}

实现阻塞功能

我们需要的阻塞效果是:1.当队列为空时再take会阻塞。2.当队列为满时再put会阻塞。

       而当前我们的代码再这两个地方只是返回一个错误。并没有阻塞。前面学习我们也知道,wait有阻塞功能,当然wait的用法也需要搭配notify和锁

代码

public class MyBlockingQueue {
    //定义一个锁对象
    Object clock = new Object();
    //采用循环队列的方式来实现阻塞队列
    volatile private int[] elem = new int[1000];
    volatile private int head;//头
    volatile private int tail;//尾
    volatile private int size;//更新当前队列中的有效数据的个数

    //put方法,来往队列里存数据
    synchronized public void put(int numb) throws InterruptedException {
        if(size == elem.length){
          //队列满了
          //  System.out.println("队列已满");
          //  return;
            clock.wait();
        }
      //新数据添加到tail位置
        elem[tail] = numb;
        tail++;
        if(tail == elem.length){
            //让下标进行循环
            tail = 0;
        }
        size++;
        clock.notify();
    }

    //take方法,从队列中取出元素
    synchronized public int take() throws InterruptedException {
        if(size == 0){
            //当前队列中没有元素
          //return -1;
            clock.wait();
        }
        //取出head下标的元素
        int ret = elem[head];
        head++;
        if(head == elem.length){
            //同理,循环下标
            head = 0;
        }
        size--;
        clock.notify();
        return ret;
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值