单例模式->饿汉模式->懒汉模式->阻塞队列->模拟实现阻塞队列->生产者消费者模型
创始人
2024-12-28 22:09:18
0

单例模式->是一种固定套路,类似于"棋谱",按照套路来,可以避免一些问题

单例模式的特点->能够保证在某个类中只存在一个实例,不会创建多个实例

饿汉模式(线程安全):最基础的单例模式,类加载的同时就会创建实例,是线程安全的

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 

  • 加锁/解锁在懒汉模式只会发生在第一次创建实例的时候,后面使用的时候就不需要加锁
  • 外层的if是判断当前是否已有实例对象
  • 内层if是判定是否需要创建对象,由于第一个if语句和第二个if语句之间 因为synchronized发生的阻塞过程中,期间可能instance被其他线程创建了实例,所以双重判定在多线程/阻塞中很有必要
  • volatile修饰instance确保多线程情况下的内存可见性和禁止指令重排序
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

BlockingQueue queue=new ArrayBlockingQueue(1000); //创建新的阻塞队列,元素类型string,元素个数1000          queue.put("abc");//put阻塞式入队列 // 需要throws声明可能会抛出异常          String elem=queue.take();//阻塞式出队列 //elem元素

 模拟实现阻塞队列

  1. 使用循环队列的方式来实现
  2. put插入元素的时候,判定如果队列满了,就进行wait(需要注意要循环进行wait,可能在被唤醒的时候队列依旧是满的)
  3. take取出元素的时候,判定如果队列为空,就进行wait(也需要循环wait,确保线程在唤醒后能够再次检查等待条件,从而避免虚假唤醒等问题)
  4.  唤醒需要注意逻辑问题,wait是在满了或者空了的情况下才会运行,并且数据不可能是又满又空的状态,所以一次只会唤醒一个wait,两个wait不会同时运行,只有put下面的notify才能唤醒take上的wait,同理也只有take下面的notify才能唤醒put的wait
  5. 注意while和读写操作都要上锁,并且是一把锁才有用

代码实现

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多个才开始消费第一个的情况

 

相关内容

热门资讯

绝活儿辅助!广西老友玩老是输怎... 绝活儿辅助!广西老友玩老是输怎么办(辅助挂)都是真的有辅助app(讲解有挂)在进入广西老友玩老是输怎...
法门辅助!福建13水插件(辅助... 法门辅助!福建13水插件(辅助挂)一贯是有辅助技巧(有挂技术)1、许多玩家不知道福建13水插件辅助怎...
办法辅助!潮友会app下载官方... 办法辅助!潮友会app下载官方辅助器(辅助挂)真是真的是有辅助app(有挂教程)该软件可以轻松地帮助...
妙招辅助!邯郸胡乐挂辅助(辅助... 妙招辅助!邯郸胡乐挂辅助(辅助挂)好像存在有辅助插件(有挂方略)1、上手简单,内置详细流程视频教学,...
教程书辅助!乐酷辅助(辅助挂)... 教程书辅助!乐酷辅助(辅助挂)其实存在有辅助脚本(有挂细节)乐酷辅助能透视中分为三种模型:乐酷辅助模...
学习辅助!决战卡五星辅助(辅助... 学习辅助!决战卡五星辅助(辅助挂)本来真的是有辅助软件(有人有挂)学习辅助!决战卡五星辅助(辅助挂)...
绝活辅助!边锋嘉兴麻将辅助器(... 绝活辅助!边锋嘉兴麻将辅助器(辅助挂)真是真的有辅助神器(新版有挂)1、边锋嘉兴麻将辅助器公共底牌简...
举措辅助!枫叶辅助器(辅助挂)... 举措辅助!枫叶辅助器(辅助挂)本来存在有辅助技巧(竟然有挂)1、下载好枫叶辅助器正确养号方法之后点击...
讲义辅助!点我达辅助(辅助挂)... 讲义辅助!点我达辅助(辅助挂)一直存在有辅助技巧(有人有挂)1、点我达辅助辅助器安装包、点我达辅助辅...
模块辅助!威信茶馆有挂的吗(辅... 模块辅助!威信茶馆有挂的吗(辅助挂)一直真的是有辅助脚本(揭秘有挂)1、玩家可以在威信茶馆有挂的吗线...