线程四

71 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

书接上文

阻塞队列

先进先出
阻塞队列同样也是一个符合先进先出规则的队列
相比于普通的队列,阻塞队列多出了几个功能
    1.线程安全
    2.阻塞效果
        1)如果队列为空,想要提取元素,那么就会阻塞,阻塞到有元素为止
        2)如果队列为满,想要插入元素,那么就会阻塞,阻塞到不满为止
这个也就引出生产者消费者模型

生产者消费者模型

这个就好像是去买橘子,如果没有橘子,那你只能等到橘子有的时候才能买,
如果橘子滞销了很久,那工厂就不生产橘子了,直到橘子被卖出去了才再生产

那阻塞队列可以做到些什么呢?
这个可以用来削峰填谷。当访问量过高的时候不会崩溃。

java使用阻塞队列

让我们先来用一用阻塞队列

public class Demo19 {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> queue = new LinkedBlockingQueue<>();
        queue.put("hello");          //这边的报错是因为阻塞引起的,因为阻塞之后就要唤醒,唤醒呢又有可能打断,所以要报错
        String s = queue.take();      //这个是拿元素
    }
}

java实现

让我们来实现一下阻塞队列

class MyBlockingQueue{
    private int[] value = new int[100];
    //保存元素个数
    private int size = 0;
    //保存元素头
    private int head = 0;
    //保存元素尾
    private int tail = 0;
    private Object object = new Object();
    public void put(int v){
        synchronized(object){
            if(size == value.length){
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                return;
            }
            value[tail] = v;
            tail++;
            size++;
            if(tail >= value.length){
                tail = 0;
            }
            object.notify();
        }
    }
    public Integer take(){
        synchronized (object){
            if(size == 0){
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                return null;
            }
            int ret = value[head];
            head++;
            size--;
            if(head >= value.length){
                head = 0;
            }
            object.notify();
            return ret;
        }
    }
}

从上面的代码可以看出几个重要的地方。

第一:上锁的位置,我们可以看到不管是take还是put我们都用到了value数组,而且没有哪个地方是没有用到的,所以我们直接全部锁上,大家可以选择这边的,还可以选择其他的方式,就比如直接在方法上面加锁。

第二:notify和wait的配合,我们发现,东西没有的时候,和东西满的时候我们都有wait让它们等待对方,当对方执行一个操作之后就会notify一下。这样wait的就可以被唤醒继续操作,这样的阻塞队列就完成了。

那么生产者消费者模型就可以按照上面写出来

private static MyBlockingQueue queue = new MyBlockingQueue();

首先我们先创建一个队列,让生产者和消费者在上面进行操作。之后我们创建生产者和消费者

生产者

Thread t1 = new Thread(()->{
    int num = 0;
    while(true){
        System.out.println("生产了: " + num);
        queue.put(num);
        num++;
    }
});
t1.start();

消费者

t1.start();
Thread t2 = new Thread(()->{
    while(true){
        System.out.println("消费了: " + queue.take());
    }
});
t2.start();

然后就能让他们进行活动了,我们可以看见,生产者生产了一个,消费者就会消费一个,有时候,生产者会连着生产,消费者也会连着消费。这个也是之前提到的抢占式执行