JAVA之阻塞队列接口结构和实现类

目录

阻塞队列

阻塞队列的核心方法:

阻塞队列的种类分析:

题目:一个初始值为零的变量,两个线程对其交替操作,一个加1一个减1

题目:线程通信之顺序调用 ReentrantLock,实现A->B->C三个线程顺序执行

题目:线程通信之生产者消费者阻塞 BlockingQueue队列版


阻塞队列

首先它是一个队列,而一个阻塞队列在数据结构中所起的作用大致如下图:

当阻塞队列是空时,从队列中获取元素的操作将会被阻塞。

当阻塞队列是满时,往队列中添加元素的操作将会被阻塞。

在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤醒

为什么需要BlockingQueue

好处是:我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都给你一手包办了。

在concurrent包发布以前,在多线程环境下,我们每个程序员都必须去自己控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。

阻塞队列的核心方法:

抛出异常

当阻塞队列满时,再往队列里add插入元素会抛出 java.lang.IllegalStateException: Queue full

当阻塞队列空时,在往队列里remove移除元素会抛出 java.util.NoSuchElementException

特殊值

插入方法,成功true 失败false

移除方法,成功返回出队列的元素,队列里没有就返回null

一直阻塞

当队列满时,生产者线程继续往队列里put元素,队列会一直阻塞生产线程直到put数据or响应中断退出

当队列空时,消费者试图从队列里take元素,队列会一直阻塞消费者线程直到队列可用

超时退出 当阻塞队列满时,队列会阻塞生产者一定时间,超过限时后生产者线程会退出
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<String>(3);
// 抛出异常
blockingQueue.add("a");// 添加
blockingQueue.remove();// 删除
blockingQueue.remove("a");// 删除
blockingQueue.element();// 检查头部元素

// 阻塞
blockingQueue.put("b");// 添加
blockingQueue.take();// 删除

// 特殊值
blockingQueue.offer("c");// 添加
blockingQueue.poll();// 删除
blockingQueue.offer("d", 3, TimeUnit.SECONDS);
blockingQueue.poll(3, TimeUnit.SECONDS);
BlockingQueue<String> blockingQueue = new SynchronousQueue<>();

new Thread(() -> {
    try {
        System.out.println(Thread.currentThread().getName() + "\tput 1");
        blockingQueue.put("a");
        System.out.println(Thread.currentThread().getName() + "\tput 2");
        blockingQueue.put("b");
        System.out.println(Thread.currentThread().getName() + "\tput 3");
        blockingQueue.put("c");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}, "AAA").start();

new Thread(() -> {
    try {
        try { TimeUnit.MILLISECONDS.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println(Thread.currentThread().getName() + "\t" + blockingQueue.take());
        try { TimeUnit.MILLISECONDS.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println(Thread.currentThread().getName() + "\t" + blockingQueue.take());
        try { TimeUnit.MILLISECONDS.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println(Thread.currentThread().getName() + "\t" + blockingQueue.take());
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}, "BBB").start();

阻塞队列的种类分析:

  1. ArrayBlockingQueue:由数组结构组成的有界阻塞队列。
  2. LinkedBlockingQueue:由链表结构组成的有界阻塞队列。(默认大小为:Integer.MAX_VALUE)
  3. priorityBlockingQueue:支持优先级排序的无界阻塞队列。
  4. DelayQueue:使用优先级队列实现的延迟无界阻塞队列。
  5. SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列。
  6. LinkedTransferQueue:由链表结构组成的无界阻塞队列。
  7. LinkedBlockingDeque:由链表结构组成的双向阻塞队列。

初始值为0的一个变量,两个线程对其交替操作,一个加1一个减1,来5轮

1、高并发 线程操作资源类

2、判断干活、唤醒通知

3、严防多线程并发状态下虚假唤醒

题目:一个初始值为零的变量,两个线程对其交替操作,一个加1一个减1

*  1、  线程      操作(方法)      资源类
*  2、  判断      干活      通知
*  3、  防止虚假唤醒机制

PS:多线程为什么要用while判断? 2个线程的情况用 if 运行正确,扩展到4个线程以上用 if 判断会出现虚假唤醒

class ShareData{
    private int number = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void incremenet()throws Exception{
        lock.lock();
        try {
            // 1    判断
            while(number != 0){
                // 等待,不能生产
                condition.await();
            }
            // 2、干活
            number++;
            System.out.println(Thread.currentThread().getName() + "\t" + number);
            // 3、 通知唤醒
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void decremenet()throws Exception{
        lock.lock();
        try {
            // 1    判断
            while(number == 0){
                // 等待,不能生产
                condition.await();
            }
            // 2、干活
            number--;
            System.out.println(Thread.currentThread().getName() + "\t" + number);
            // 3、 通知唤醒
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}
/**
 * 题目:一个初始值为零的变量,两个线程对其交替操作,一个加1一个减1
 *  1、  线程      操作(方法)      资源类
 *  2、  判断      干活      通知
 *  3、  防止虚假唤醒机制
 */
public class BlockingQueueDemo {

    public static void main(String[] args) {
        ShareData shareData = new ShareData();
        new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                try {
                    shareData.incremenet();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();
        
        new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                try {
                    shareData.decremenet();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"BB").start();
    }
}

 

题目:线程通信之顺序调用 ReentrantLock,实现A->B->C三个线程顺序执行


/**
 * 多线程之间顺序调用,实现A->B->C三个线程启动,要求如下;
 * AA打印5次,BB打印10次,CC打印15次
 * 5循环10次
 */

class ShareResource{
    private int number = 1; // A:1 B:2 C:3
    private Lock lock = new ReentrantLock();
    private Condition c1 = lock.newCondition();
    private Condition c2 = lock.newCondition();
    private Condition c3 = lock.newCondition();

    public void print5(){
        lock.lock();
        try {
            // 1、判断
            while (number != 1){
                c1.await();
            }
            // 2、干活
            for (int i = 1; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }
            // 3、通知
            number = 2; // 修改标志位
            c2.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void print10(){
        lock.lock();
        try {
            // 1、判断
            while (number != 2){
                c2.await();
            }
            // 2、干活
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }
            // 3、通知
            number = 3; // 修改标志位
            c3.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void print15(){
        lock.lock();
        try {
            // 1、判断
            while (number != 3){
                c3.await();
            }
            // 2、干活
            for (int i = 1; i <= 15; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }
            // 3、通知
            number = 1; // 修改标志位
            c1.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}
public class SyncAndReentrantLockDemo {
    public static void main(String[] args) {
        ShareResource shareResource = new ShareResource();
        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                shareResource.print5();
            }
        },"AA").start();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                shareResource.print10();
            }
        },"BB").start();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                shareResource.print15();
            }
        },"CC").start();
    }
}

题目:线程通信之生产者消费者阻塞 BlockingQueue队列版

class MyResource{
    private volatile boolean FLAG = true;   // 默认开启,进行生产+消费
    private AtomicInteger atomicInteger = new AtomicInteger();
    BlockingQueue<String> blockingQueue = null;

    // 构造注入方法
    public MyResource(BlockingQueue<String> blockingQueue) {
        this.blockingQueue = blockingQueue;
        System.out.println(blockingQueue.getClass().getName());
    }

    public void myProd()throws Exception{
        String data = null;
        boolean retValue = false;
        while(FLAG){
            data = atomicInteger.incrementAndGet() + "";
            retValue = blockingQueue.offer(data, 2L, TimeUnit.SECONDS);
            if(retValue){
                System.out.println(Thread.currentThread().getName() + "\t插入队列" + data +"成功");
            }else{
                System.out.println(Thread.currentThread().getName() + "\t插入队列" + data +"失败");
            }
            try { TimeUnit.MILLISECONDS.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
        }
        System.out.println(Thread.currentThread().getName() + "\t大老板叫停,表示flag=false,生产动作结束");
    }

    public void myConsumer()throws Exception{
        while (FLAG){
            String result = blockingQueue.poll(2, TimeUnit.SECONDS);
            if(null == result || result.equalsIgnoreCase("")){
                FLAG = false;
                System.out.println(Thread.currentThread().getName() + "\t超过2秒钟没有取到蛋糕,消费退出");
                System.out.println();
                System.out.println();
                return;
            }
            System.out.println(Thread.currentThread().getName() + "\t消费蛋糕" + result + "成功");
        }
    }

    public void stop()throws Exception{
        this.FLAG = false;
    }
}
/**
 * 线程通信之生产者消费者阻塞队列版
 */
public class ProdConsumer_BlockQueueDemo {

    public static void main(String[] args) {
        MyResource myResource = new MyResource(new ArrayBlockingQueue<>(10));
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t生产线程启动");
            try {
                myResource.myProd();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "Prod").start();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t消费线程启动");
            try {
                myResource.myConsumer();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "Consumer").start();

        try { TimeUnit.MILLISECONDS.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println();
        System.out.println();
        System.out.println("5秒钟到,大老板mian线程叫停,活动结束");

        try {
            myResource.stop();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章