阻塞队列知道吗?

阻塞队列的概念

阻塞队列的工作原理图如下
阻塞队列知道吗?_第1张图片
当阻塞队列是时,从队列中获取元素的操作会被阻塞
当阻塞队列是时,从队列中添加元素的操作会被阻塞

为什么要使用阻塞队列呢

首先在多线程领域下,阻塞是指某些情况下会挂起线程,一旦条件满足,被挂起的线程又会自动被唤醒
使用了阻塞队列后
我们不需要手动的去唤醒,和阻塞,控制这些小细节 待会下面的生产者-消费者代码会带你感受其中的便捷

介绍下BlockingQueue的核心方法

阻塞队列知道吗?_第2张图片

阻塞队列的种类分析

ArrayBlockingQueue :由数组结构组成的有界阻塞队列
LinkedBlockingQueue :由链表结构组成的有界(大小默认值为Integer.MAX_VALUE)阻塞队列

SynchronousQueue :不存储元素的阻塞队列,也即单个元素的队列 (每put 一个后必须消费take一个才能继续put)

/**
 * SynchronousQueue案例
 */
public class SynchronousQueueDemo {
     


    public static void main (String[] args) {
     
        SynchronousQueue< 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 (InterruptedException e) {
     
                e.printStackTrace();
            }
        },"A").start();


        new Thread(() -> {
     
            try {
     
                Thread.sleep(3000);
                System.out.println(Thread.currentThread().getName() + "\t take" + blockingQueue.take());

                Thread.sleep(3000);
                System.out.println(Thread.currentThread().getName() + "\t take" + blockingQueue.take());

                Thread.sleep(3000);
                System.out.println(Thread.currentThread().getName() + "\t take" + blockingQueue.take());
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }
        },"B").start();
    }

}


结果
阻塞队列知道吗?_第3张图片

阻塞队列用在哪里

可以用在生产者消费者模式 ;线程池 ; 消息中间件

一、下面演示几种生产者消费的写法

采用synchronized实现

package com.demo.ProducerConsumerModel;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 生产者消费者模式实现
 * 1、synchronized实现
 */
public class SynchronizedDemo {
     
    public static void main (String[] args) {
     
        SynchronizedCache synchronizedCache = new SynchronizedCache();
        //3个线程去生产
        for (int i = 0; i < 3; i++) {
     
            new Thread(() -> {
     
                try {
     
                    synchronizedCache.producer();
                } catch (InterruptedException e) {
     
                    e.printStackTrace();
                }
            }).start();
        }
        //3个线程在消费
        for (int i = 0; i < 3; i++) {
     
            new Thread(() -> {
     
                try {
     
                    synchronizedCache.consumer();
                } catch (InterruptedException e) {
     
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

/**
 * 资源类
 */
class SynchronizedCache {
     
    /**
     * 数据
     */
    private AtomicInteger data = new AtomicInteger(0);
    private Object object = new Object();
    /**
     * 生产者
     *
     * @throws InterruptedException
     */
    public void producer () throws InterruptedException {
     
        synchronized(object){
     
            //防止虚假唤醒,所有的唤醒操作都应该放在while 目的是为了唤醒后 再判断一次
            while (data.get() != 0) {
     
                //等待 停止生产
                object.wait();
            }
            //开始生产
            data.getAndIncrement();
            System.out.println(Thread.currentThread().getName() + "生产了数据");
            //唤醒阻塞的线程
            object.notify();
        }

    }

    public void consumer () throws InterruptedException {
     
        synchronized(object){
     
            while (data.get() == 0) {
     
                //等待 停止消费
                object.wait();
            }

            //开始消费
            data.getAndDecrement();
            System.out.println(Thread.currentThread().getName() + "消费了数据");

            //唤醒阻塞的线程
            object.notify();
        }

    }

}


结果

阻塞队列知道吗?_第4张图片

Lock版本 可以通过condition 进行精确唤醒

package com.demo.ProducerConsumerModel;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 生产者消费者模式实现
 * 2、Lock
 */
public class LockDemo {
     
    public static void main (String[] args) {
     
        LockCache lockCache = new LockCache();
        //3个线程去生产
        for (int i = 0; i < 3; i++) {
     
            new Thread(() -> {
     
                lockCache.producer();
            }).start();
        }
        //3个线程在消费
        for (int i = 0; i < 3; i++) {
     
            new Thread(() -> {
     
                lockCache.consumer();
            }).start();
        }
    }
}

/**
 * Lock资源类
 */
class LockCache {
     
    /**
     * 数据
     */
    private AtomicInteger data = new AtomicInteger(0);

    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    /**
     * 生产者
     */
    public void producer () {
     
        lock.lock();
        try {
     
            while (data.get() != 0) {
     
                condition.await();
            }
            //开始生产数据

            data.getAndIncrement();
            System.out.println(Thread.currentThread().getName() + "生产了数据");
            //唤醒其他线程

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

    /**
     * 消费者
     */
    public void consumer () {
     
        lock.lock();
        try {
     
            while (data.get() == 0) {
     
                condition.await();
            }
            //开始消费数据

            data.getAndDecrement();
            System.out.println(Thread.currentThread().getName() + "消费了数据");
            //唤醒其他线程

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

阻塞队列知道吗?_第5张图片

阻塞队列版本

package com.demo.ProducerConsumerModel;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
 * 生产者消费者模式实现
 * 3、阻塞队列实现
 */
public class BlockingQueueDemo {
     
    public static void main (String[] args) {
     
        BlockingQueueCache blockingQueueCache = new BlockingQueueCache();
        for (int i = 0; i < 5; i++) {
     
            new Thread(()->{
     
                try {
     
                    blockingQueueCache.producer();
                } catch (InterruptedException e) {
     
                    e.printStackTrace();
                }
            }).start();
        }


        for (int i = 0; i < 5; i++) {
     
            new Thread(()->{
     
                try {
     
                    blockingQueueCache.consumer();
                } catch (InterruptedException e) {
     
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

/**
 * 资源类
 */
class BlockingQueueCache {
     
    private BlockingQueue blockingQueue = new ArrayBlockingQueue(1);

    /**
     * 生产者
     */
    public void producer () throws InterruptedException {
     
        //生产一个数据
        blockingQueue.put(1);
        System.out.println(Thread.currentThread().getName() + "生产了一个数据");
    }


    /**
     * 消费者
     */
    public void consumer () throws InterruptedException {
     
        //生产一个数据
        blockingQueue.take();
        System.out.println(Thread.currentThread().getName() + "消费了一个数据");
    }
}

三个版本对比 可以看出用了阻塞队列 可以帮助程序员少做一些细节的判断,简化了开发

二、线程池

线程池部分主要放在下一篇博客讲解,这里讲一下 Callable 怎么实现多线程

package com.demo;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * 实现多线程的方式:实现Callable接口
 */
public class CallableDemo {
     
    public static void main (String[] args) throws ExecutionException, InterruptedException {
     
        //将Callable包装到FutureTask中
        FutureTask<Integer> futureTask = new FutureTask<>(new MyThread());
        new Thread(futureTask, "A").start();
        new Thread(futureTask, "B").start(); //如果是同一个task,即使多个线程,也只进call()一次
        System.out.println(Thread.currentThread().getName() + "...");
        Integer result = futureTask.get();  //获取callable线程的计算结果,如果没有完成计算,会导致堵塞,直到计算完成
        System.out.println("result:" + result);

    }
}

class MyThread implements Callable {
     

    @Override
    public Object call () throws Exception {
     
        System.out.println("Callable .....");
        return 1024;
    }
}

你可能感兴趣的:(面试题集合)