JUC-阻塞队列

为什么要用阻塞队列?有什么好?

阻塞就是线程挂起,当满足条件后,又被唤醒。
为什么需要BlockingQueue?

  1. 不再需要关心线程阻塞和唤醒的时机,因为BlockQueue包办了这个细节
  2. 在阻塞队列出现前,程序员必须手动控制这些细节,兼顾效率和线程安全,增加了开发难度

实现类

  1. *ArrayBlockingQueue:由数组结构组成的有界阻塞队列
  2. *LinkedBlockingQueue: 由链表结构组成的有界阻塞队列(但默认大小为Integer.MAX_VALUE,大小配置可选)
  3. PriorityBlockingQueue: 支持优先级排序的无界阻塞队列
  4. DelayQueue: 使用优先级队列实现的延迟无界阻塞队列(内部采用PriorityQueue(排序)与ReentrantLock(锁)实现)
  5. *SynchronousQueue: 队列只插入一个元素,同步队列
  6. LinkedBlockingDeque:由链表结构组成的双向阻塞队列
    带*是重要的,是线程池的底层实现。

BlockQueue的一些方法

添加元素到队列中,区别就在于当队列已经满的时候,此时
public boolean add(E e) 方法将抛出IllegalStateException异常,说明队列已满。
public boolean offer(E e) 方法则不会抛异常,只会返回boolean值,告诉你添加成功与否,队列已满,当然返回false。
public void put(E e) throws InterruptedException 方法则一直阻塞(即等待,直到元素可以加入队列为止)

同理remove,poll, take三种移除队列中线程的方法只在队列为空的时候有区别, remove为抛异常,poll为返回boolean值, take等待直到有线程可以被移除。
如下图:
在这里插入图片描述
三套操作:插入、移除、检查。
四套处理:抛异常,返回特殊值,阻塞,超时

生产者消费者模式的传统版实现?(synchronized版和Lock版)

synchronized版本

package com.yc.blockQueue;

public class ProdConsumer_demo1 {

    public static void main(String[] args) {
        Number1 number = new Number1();

        new Thread(() -> {
            for (int i = 0; i < 20; i++) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                number.plus();
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                number.reduce();
            }
        }, "B").start();
    }

}

class Number1 {
    private int num = 0;

    public synchronized void plus() {
        //使用while 防止线程虚假唤醒  当线程被唤醒后 需要重新验证程序
        while (num >= 1) {
            System.out.println("商品已满");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        num++;
        System.out.println(Thread.currentThread().getName() + "plus:" + num);
        //notify表示唤醒一个线程,,notifyAll也表示唤醒一个线程,但它会notify所有的线程,具体唤醒哪一个线程,由jvm来决定
        this.notifyAll();
    }

    public synchronized void reduce() {
        //使用while 防止线程虚假唤醒  当线程被唤醒后 需要重新验证程序
        while (num <= 0) {
            System.out.println("缺货");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        num--;
        System.out.println(Thread.currentThread().getName() + "reduce:" + num);
        this.notifyAll();
    }
}

lock版本:

package com.yc.blockQueue;

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

public class ProdConsumer_demo2 {
    public static void main(String[] args) {
        Number2 number = new Number2();

        new Thread(() -> {
            for (int i = 0; i < 20; i++) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                number.plus();
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 0; i < 20; i++) {
                number.reduce();
            }
        }, "B").start();
    }

}

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

    public void plus() {
        lock.lock();
        try {
            //使用while 防止线程虚假唤醒  当线程被唤醒后 需要重新验证程序
            while (num >= 1) {
                System.out.println("商品已满");
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            num++;
            System.out.println(Thread.currentThread().getName() + "plus:" + num);
            //signal是唤醒其中一个等待唤醒的线程,signalAll()是唤醒所有的。
            condition.signalAll();
        } catch (Exception e) {

        } finally {
            lock.unlock();
        }

    }

    public void reduce() {
        //使用while 防止线程虚假唤醒  当线程被唤醒后 需要重新验证程序
        lock.lock();
        try {
            while (num <= 0) {
                System.out.println("缺货");
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            num--;
            System.out.println(Thread.currentThread().getName() + "reduce:" + num);
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}


生产者消费者模式的阻塞队列版实现?

package com.yc.shangguigu;

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

class MyResource{

	//多线程下的交互 需要可见性volatile
	private volatile boolean flag=true;// 默认开始  进行生产+消费
	//多线程下不用++  线程不安全
	private AtomicInteger  atomicInteger =new AtomicInteger();
	//进行接口适配   要求任何实现此接口的类 都能使用
	BlockingQueue<String> blockingQue=null;
	//传接口 不能传具体的实现类
	public MyResource(BlockingQueue<String> blockingQue) {
		this.blockingQue = blockingQue;
		System.out.println("日志:"+blockingQue.getClass().getName());
	}
	public void myPro() throws Exception{
		String data=null;
		boolean retValue;
		while(flag){
			//Atomically increments by one the current value.
			data=atomicInteger.incrementAndGet()+"";
			retValue=blockingQue.offer(data,2L,TimeUnit.SECONDS);
			if(retValue){
				System.out.println(Thread.currentThread().getName()+"插入成功");
			}else{
				System.out.println(Thread.currentThread().getName()+"插入失败");
			}
			TimeUnit.SECONDS.sleep(1);
		}
		System.out.println(Thread.currentThread().getName()+"大老板叫停 生产动作结束");
	}
	public void myCon() throws Exception{
		String result=null;
		while(flag){
			//2秒钟等不到我就不取了
			result=blockingQue.poll(2,TimeUnit.SECONDS);
			if(null==result||result.equalsIgnoreCase("")){
				flag=false;
				System.out.println(Thread.currentThread().getName()+"超时2秒钟没有取到蛋糕 我退出");
				//如果不返回  消费者还会再取一次  会出现null值
				return ;
			}
			System.out.println(Thread.currentThread().getName()+"获取成功: "+result);
		}
	}
	
	public void stop(){
		this.flag=false;
	}
	
}
/*使用阻塞队列 不需要另外加lock 获sychronized
 * 我们不需要关心什么时候需要阻塞线程,什么是够需要唤醒线程,因为这一切BlockingQueue都已经一手包办了
 * 
 * */
public class ProAndCon3_Blockqueue {
	public static void main(String[] args) {
		 MyResource mydata=new MyResource(new ArrayBlockingQueue<>(10));
		 
		 new Thread(()->{
			 System.out.println(Thread.currentThread().getName()+"生产线程启动");
			 try {
				mydata.myPro();
			} catch (Exception e) {
				e.printStackTrace();
			}
		 },"product").start();
		 
		 new Thread(()->{
			 System.out.println(Thread.currentThread().getName()+"消费线程启动");
			 System.out.println();
			 System.out.println();	
			 try {
				mydata.myCon();
			} catch (Exception e) {
				e.printStackTrace();
			}
		 },"consume").start();
		 
		 try {
			TimeUnit.SECONDS.sleep(5);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		 System.out.println("5秒鈡時間到 老板叫停");
		 mydata.stop();
		 
	}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值