【java并发系列】java BlockingQueue学习

大家好,我是walker
一个从文科自学转行的程序员~
爱好编程,偶尔写写编程文章和生活
欢迎关注公众号【I am Walker】,回复“电子书”,就可以获得200多本编程相关电子书哈~
我的gitee:https://gitee.com/shen-chuhao/walker.git 里面很多技术案例!

什么是阻塞队列?

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。
这两个附加的操作是:

  • 在队列为空时,获取元素的线程会等待队列变为非空。
  • 当队列满时,存储元素的线程会等待队列可用。

应用场景

阻塞队列常用于生产者和消费者的场景,

  • 生产者是往队列里添加元素的线程,
  • 消费者是从队列里拿元素的线程。
  • 阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。

阻塞队列使用最经典的场景就是 socket 客户端数据的读取和解析,读取数据的线程不断将数据放入队列,然后解析线程不断从队列取数据解析。

如何使用阻塞队列来实现生产者-消费者模型?

案例:生产包子,消费包子

生产者

package productConsumerMode.blockingQueue;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 生产者
 */
public class Producer implements Runnable {
    //该字段用于做是否循环的字段  这里使用volatile保证可见性和防止指令重排
    private volatile boolean isRunning = true;
    //线程休眠时间,防止线程调用速度太快
    private final static int SLEEP_TIME = 1000;
    //包子阻塞队列,用来存储生产的包子
    private BlockingQueue<String> bumQueue;
    //包子序号
    private static AtomicInteger serial = new AtomicInteger();

    /**
     * 构造方法
     * 参数为:包子队列
     */
    public Producer(BlockingQueue<String> bumQueue) {
        this.bumQueue = bumQueue;
    }


    /**
     * 重写run方法
     */
    @Override
    public void run() {
        //当isRunning=true的时候,循环进行生产
        while (isRunning) {
            try {
                /**
                 * boolean offer(E e, long timeout, TimeUnit unit) 若2秒还没有加入,则代表队列阻塞,这时候打印加入队列失败,否则则加入队列
                 */
                if (!bumQueue.offer("包子" + serial.getAndAdd(1), 2, TimeUnit.SECONDS)) {
                    System.out.println("队列的数量为:" + bumQueue.size() + ",无法加入队列");
                } else {
                    System.out.println("【生产者】" + Thread.currentThread().getName() + "生产包子" + serial.get());
                }
                //线程休眠
                Thread.sleep(SLEEP_TIME);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }


    public void stopProduct() {
        isRunning = false;
    }
}

消费者

package productConsumerMode.blockingQueue;

import java.util.concurrent.BlockingQueue;

/**
 * 消费者
 */
public class Consumer implements Runnable {
    //包子队列
    private BlockingQueue<String> bumQueue;
    //是否支持消费
    private volatile boolean isConsumer=true;

    //构造方法
    public Consumer(BlockingQueue<String> bumQueue) {
        this.bumQueue = bumQueue;
    }

    @Override
    public void run() {

        while(isConsumer){
            //从队列中获取包子
            String bum = bumQueue.poll();

            //若包子存在,则消费  若不存在则提示没有包子消费
            if (bum != null) {
                System.out.println("【消费者】" + Thread.currentThread().getName() + "消费" + bum);
            }else{
                System.out.println("包子数量剩余"+bumQueue.size()+",没有包子可以消费");
            }
            //此处让线程休眠一下,防止消费太快
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void stopConsumer(){
        isConsumer=false;
    }
}

测试

package productConsumerMode.blockingQueue;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * @Author: WalkerShen
 * @DATE: 2022/3/16
 * @Description: 使用blockingQueue实现生产者消费者模式
 *  案例:生产者生产包子  消费者买包子
 **/
public class BlockingQueueTest {

    public static void main(String[] args) throws InterruptedException {
        //创建一个阻塞队列,容量为10
        BlockingQueue<String> bumQueue=new LinkedBlockingQueue<>(10);
        //创建Runnable
        Producer producer1 = new Producer(bumQueue);
        Producer producer2 = new Producer(bumQueue);
        Consumer consumer1 = new Consumer(bumQueue);
        Consumer consumer2 = new Consumer(bumQueue);

        //使用Executors工具执行线程
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(consumer1);
        executorService.execute(consumer2);
        executorService.execute(producer1);
        executorService.execute(producer2);


        //让主线程休眠20秒,这里是为了查看执行的结果
        Thread.sleep(20*1000);

        //停止生产和消费
        producer1.stopProduct();
        producer2.stopProduct();
        consumer1.stopConsumer();
        consumer2.stopConsumer();

        //最后关闭线程池
        executorService.shutdown();

    }


}

JDK7 提供的阻塞队列

ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
PriorityBlockingQueue :一个支持优先级排序无界阻塞队列。
DelayQueue:一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue:一个不存储元素的阻塞队列。
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

消费者模式jdk5前后的变化

  • Java 5 之前实现同步存取时,可以使用普通的一个集合,然后在使用线程的协作和线程同步可以实现生产者,消费者模式,主要的技术就是用好,wait,notify,notifyAll,sychronized 这些关键字。
  • 而在 java 5 之后,可以使用阻塞队列来实现,此方式大大简少了代码量,使得多线程编程更加容易,安全方面也有保障。

源码解析

ArrayBlockingQueue

add

  public boolean add(E e) {
        return super.add(e);
    }

add

 public boolean add(E e) {
        if (offer(e))
            return true;
     //会抛出队列满的异常
        else
            throw new IllegalStateException("Queue full");
    }

offer
 public boolean offer(E e) {
     //检查是否为null
        checkNotNull(e);
     //加锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //如果
            if (count == items.length)
                return false;
            else {
                enqueue(e);
                return true;
            }
        } finally {
            //解锁
            lock.unlock();
        }
    }
enqueue
 private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
     //结果放入
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
     
     //private final Condition notEmpty; 使用Condition,释放信号,让其他线程获取
        notEmpty.signal();
    }

offer

public boolean offer(E e) {
        checkNotNull(e);
    
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count == items.length)
                return false;
            else {
                enqueue(e);
                return true;
            }
        } finally {
            lock.unlock();
        }
    }

put

 public void put(E e) throws InterruptedException {
        checkNotNull(e);
     
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            //如果队列满的话,会阻塞
            while (count == items.length)
                notFull.await();
            //否则则直接加入队列
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }

remove:返回true,false

public boolean remove(Object o) {
    //如果为null,则返回false
        if (o == null) return false;
    
        final Object[] items = this.items;
    //加锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //如果队列长度大于0,才可以进行移除
            if (count > 0) {
                final int putIndex = this.putIndex;
                int i = takeIndex;
                do {
                    //循环:如果找到对应的值,则移除,否则则继续循环,i++ 坐标++
                    if (o.equals(items[i])) {
                        removeAt(i);
                        return true;
                    }
                    //如果坐标等于数组产长度,则从0开始
                    if (++i == items.length)
                        i = 0;
                } while (i != putIndex);
            }
            //否则直接返回false
            return false;
        } finally {
            lock.unlock();
        }
    }

poll:返回null和结果

public E poll() {
    //加锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //count是否等于0,如果等于0则返回null
            return (count == 0) ? null : dequeue();
        } finally {
            //解锁
            lock.unlock();
        }
    }

dequeue

private E dequeue() {
    
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
    //通知
        notFull.signal();
        return x;
    }

take

  public E take() throws InterruptedException {
      //异常中断加锁 lockInterruptibly
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            //如果队列长度为0的话,则阻塞等待获取
            while (count == 0)
                notEmpty.await();
            //否则则
            return dequeue();
        } finally {
            //解锁
            lock.unlock();
        }
    }

dequeue

private E dequeue() {
        // assert lock.getHoldCount() == 1;
        // assert items[takeIndex] != null;
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal();
        return x;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WalkerShen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值