单例模式->是一种固定套路,类似于"棋谱",按照套路来,可以避免一些问题
单例模式的特点->能够保证在某个类中只存在一个实例,不会创建多个实例
public class Singleton { // 在类加载时就完成了实例化,避免了线程同步的问题 private static Singleton instance = new Singleton(); private Singleton() {}// 私有构造函数,防止被外部实例化 public static Singleton getInstance() {// 获取单例对象的静态方法 return instance; } }
public class SingleLaze { private static volatile SingleLaze instance = null; private SingleLaze() {} // 私有构造函数,防止外部实例化 public static SingleLaze getInstance() { if (instance == null) { //首次调用get方法instance==null才会创建实例 instance = new SingleLaze(); } return instance; } }
两个懒汉模式横向对比可以发现,修改后的加上了锁和双重if判定以及给instance加上lvolatile
public class SingleLaze { private static volatile SingleLaze instance = null; private SingleLaze() {} // 私有构造函数,防止外部实例化 public static SingleLaze getInstance() { if (instance == null) { // 第一次检查,避免已有实例对象,再次创建新的对象情况 synchronized (SingleLaze.class) { // 使用类对象作为锁,里面表示了singlelaze这个类 if (instance == null) { // 第二次检查,确保单例 instance = new SingleLaze(); } } } return instance; } }
阻塞队列是什么?
- 阻塞队列是一种线程安全的数据结构,并具有以下特性
- 当队列空的时候,出队列操作就会阻塞,直到有元素入队列
- 当队列满的时候,入队列操作就会阻塞,直到有元素出队列
为什么要用阻塞队列?
在包饺子的场景中,如果不用阻塞队列,擀饺子皮的一直擀,但是包饺子的包的很慢,会出现饺子皮摆不下桌子的情况,如果用到阻塞队列,擀的人快,在桌子摆不下的情况就会阻塞等待,直到桌子能摆的下再擀,而包饺子快的情况下,就会等有饺子皮再包
BlockingQueue queue=new ArrayBlockingQueue(1000); //创建新的阻塞队列,元素类型string,元素个数1000 queue.put("abc");//put阻塞式入队列 // 需要throws声明可能会抛出异常 String elem=queue.take();//阻塞式出队列 //elem元素
代码实现
public class MyBlockingQueue1 { //为了简单,不写作泛型,只考虑元素类型是string private String[]elems=null; private int head=0;//指向头结点下标 private int tail=0;//指向尾节点下标 private int size=0;//当前数组所存的元素个数 //准备锁对象 private Object locker =new Object(); public MyBlockingQueue1(int capacity){//使用构造函数指定阻塞队列的最大容量 elems=new String[capacity]; } public void put(String elem) throws InterruptedException { synchronized (locker){ while (size>=elems.length){ locker.wait();//当前所占容量等于或超过最大容量,就阻塞等待 } //没满 elems[tail++]=elem;//将传入的元素从尾节点插入到数组,同时尾节点++ if(tail>= elems.length){ tail=0; } size++;//元素个数++ //入队成功后唤醒可能在take时候阻塞的wait locker.notify(); } } public String take() throws InterruptedException { String elem=null;//在锁里面创建对象就在锁外面返回不了 synchronized (locker){ while (size==0){ locker.wait();//当前数组元素为0,就阻塞等待 } //没空 elem=elems[head++]; if(head>= elems.length){ head=0; } size--;//元素个数++ //入队成功后唤醒可能在take时候阻塞的wait locker.notify(); } return elem; } public static void main(String[] args) throws InterruptedException { MyBlockingQueue1 queue=new MyBlockingQueue1(1000); queue.put("aaa"); queue.put("bbb"); queue.put("ccc"); queue.put("ddd"); String elem= queue.take(); System.out.println("elem="+elem); elem= queue.take(); System.out.println("elem="+elem); elem= queue.take(); System.out.println("elem="+elem); elem= queue.take(); System.out.println("elem="+elem); } }
运行结果
只需要更改主函数内容
public static void main(String[] args) { MyBlockingQueue1 queue=new MyBlockingQueue1(1000); //生产者 Thread t1=new Thread(()->{ int n=1; while (true){ try { queue.put(n+""); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("生产元素"+n); n++; } }); //消费者 Thread t2=new Thread(()->{ while (true){ try { String n= queue.take(); System.out.println("消费元素"+n); // Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }); t1.start(); t2.start(); } }
如果在生产后加上延时,消费后不加延时,就会出现生产一个消费一个的情况
如果在消费后面加上延时而生产后面不加延时,就会出现生产了1000多个才开始消费第一个的情况