JAVA进阶 THREAD学习09 多线程案例--阻塞队列

阻塞队列的基本理解

  • 和队列一样,”先进先出“
  • 输入阻塞,当队列里的数据已经将队列塞满时,=输入会阻塞
  • 输出阻塞,当队列里是的时候,输入会阻塞

经典应用–消费者/生产者模型

这里先给一个例子:
消费平台在双十一等等促销日时会出现”秒杀“的情况。这意味着在短时间服务器需要处理大量的数据,服务器的压力会特别大,也会带来如下问题:

  1. 资源占用过高。因为需要频繁的申请、释放资源,会增加系统的开销,降低整体的处理效率。可以理解为你有一堆事要做,虽然每件事都办的很快,但是每件事都在不同的地点。所以每一件事情中10分钟给路程,1分钟办完。时间也是浪费地很多。
  2. 硬件资源分配不均。从第一点可以看出用于申请、释放资源的硬件部分(CPU核心等)被过度使用,但是内存却在闲置。
  3. 系统的稳定性降低。过快的处理会导致系统出现静态条件(多线程/并发访问、修改共享数据时,因为访问的次序不同,导致数据不一)。

阻塞队列的优点:

  1. 充当缓冲区,缓解服务器的压力

阻塞队列会让需要操作的数据处在一个较为稳定的范围内(队列的长度范围内),控制数据的操作速率,解决了问题1和2.而且阻塞队列会将所需要操作的数据进行排序,保证了访问次序,解决了问题3.
实现"消峰“,放置服务器被一瞬间冲垮。

  1. **解耦 **强耦合的对象

就好比大家一起包饺子,一拨人擀皮,一拨人包。
擀皮的人不需要知道下一个是谁用他擀的皮去包饺子,也不需要知道包的饺子是什么样子的,他只需要擀好了就往盘子上放就好了。
包饺子的人不需要知道是谁擀的皮,也不需要知道擀皮的人是用擀面杖擀、用罐头擀或者是直接去超市买的,他只需要把盘子里的皮过来包就行了。
这里的盘子就是我们所说的阻塞队列。

标准库里的阻塞队列BlockingQueue

  • BlockingQueue其实是接口,实现的类是LinkedBlockingQueue
  • 阻塞方法:==put ==用于阻塞式的入队列,==take ==用于阻塞式的出队列
  • 非阻塞方法:offer,pull,pick

基本要素:

  • BlockingQueue < String> blockingQueue=new LinkedBlockingQueue<>();
  • blockingQueue.put(“阻塞队列”);
  • String get=blockingQueue.take();
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class JAVA_THREAD09 {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<Integer>();
        Thread customer = new Thread(() -> {
            while (true) {
                try {
                    int value = blockingQueue.take();
                    System.out.println("消费元素: " + value);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "消费者");
        customer.start();

        Thread producer = new Thread(() -> {
            Random random = new Random();
            while (true) {
                try {
                    int num = random.nextInt(1000);
                    System.out.println("生产元素: " + num);
                    blockingQueue.put(num);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "生产者");
        producer.start();

        customer.join();
        producer.join();
    }
}

结果如下:
在这里插入图片描述

阻塞队列的基本实现

四变量

  • 一存储
    • volatile int[] item (这里的int类型可以换成任何想要存储的类型,这里使用循环数组来实现队列)
  • 三标识
    • volatile int size (用于记录队列长度,保证可见性)
    • int tail (put方法需要 )
    • int head (take方法需要 )

二方法

方法都主要是
2个基本状态:可执行、不可执行
4个基本内容:存、移、变、释

  • put() 方法
    • 不可执行(队列已满,size==item.length)
      • 循环等待 wait()
    • 可执行(队列未满,size!=item.length)
      • 存 将 put 进入的数传入数组 (item[tail]=value)
      • 移 移动 tail 的位置(tail=(tail++)%item.length) 模上长度是利用循环数组实现队列
      • 变 队列的长度发生变化(size++)
      • 释 释放所有线程(notifyAll() )

  • take() 方法
    新建返回变量 ret=0;
    • 不可执行(队列空,size==0)
      • 循环等待 wait()
    • 可执行(队列不空,size!=0)
      • 存 将队列头的数存入返回变量里 (ret=item[head])
      • 移 移动 head 的位置(head =(head ++)%item.length)
      • 变 队列长度发生改变(size–)
      • 释 释放所有线程(notifyAll() )

最后再返回ret

看看阻塞队列基本实现的代码

static class BlockingQueue {
        private int[] items = new int[1000];
        private volatile int size = 0;
        private int head = 0;
        private int tail = 0;

        public void put(int value) throws InterruptedException {
            synchronized (this) {
                // 此处最好使用 while.
				// 否则 notifyAll 的时候, 该线程从 wait 中被唤醒,
				// 但是紧接着并未抢占到锁. 当锁被抢占的时候, 可能又已经队列满了
				// 就只能继续等待
                while (size == items.length) {
                    wait();
                }
                items[tail] = value;
                tail = (tail + 1) % items.length;
                size++;
                notifyAll();
            }
        }

        public int take() throws InterruptedException {
            int ret = 0;
            synchronized (this) {
                while (size == 0) {
                    wait();
                }
                ret = items[head];
                head = (head + 1) % items.length;
                size--;
                notifyAll();
            }
            return ret;
        }

        public synchronized int size() {
            return size;
        }
    }


    // 测试代码
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue blockingQueue = new BlockingQueue();
        Thread customer = new Thread(() -> {
            while (true) {
                try {
                    int value = blockingQueue.take();
                    System.out.println(value);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "消费者");
        customer.start();
        Thread producer = new Thread(() -> {
            Random random = new Random();
            while (true) {
                try {
                    blockingQueue.put(random.nextInt(1000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "生产者");
        producer.start();
        customer.join();
        producer.join();
    }

结果:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值