单例模式&阻塞队列详解

单例模式

单例模式:程序中一个类只有一个实例对象

懒汉模式(类在加载的时候就已经被创建)

java 复制代码
public class SingletonHungry {
    // private保证这个类不会被外面修改
    //static静态代码表示这个类全局唯一
    private static SingletonHungry singletonHungry = new SingletonHungry();
    //构造方法私有化,防止其他程序创建当前类的实例
    private SingletonHungry() {}
    //获取实例
    private static SingletonHungry getInstance() {
        return singletonHungry;
    }
}

饿汉模式(使用的时候再被创建)

java 复制代码
public class SingletonLazy {
    private static volatile SingletonLazy singletonLazy = null;
    private SingletonLazy() {

    }
    public static SingletonLazy getInstance() {
        //第一次判断是否需要加锁,加锁是一个很耗资源的事情
        if(singletonLazy == null) {
            //加锁防止多个线程都在创建前判断为空,而导致都去创建
            synchronized (SingletonLazy.class) {
                //判断是否为空
                if(singletonLazy == null) {
                    singletonLazy = new SingletonLazy();
                }
            }
        }
        return singletonLazy;
    }
}

这种双重检查的操作叫做DCL

只要是多线程环境中修改了共享变量,就要给共享变量加volatile,通过synchronized原子性本身已经保证了内存可见性

这里类加volatile主要是考虑到指令重排序的问题

创建对象的过程可分为:

1.在内存中申请一片空间

2.初始化对象的属性

3.把对象在内存中的首地址赋给对象的引用

由于1执行之后就可以执行3,所以指令重排序可能是:1 3 2,但此时类没有初始化,就可能出现一些问题

阻塞队列

阻塞队列是一种特殊的队列,也遵循先进先出的原则。

阻塞队列也是一种线程安全的数据结构。并且:

当队列满的时候,继续入队就会阻塞,直到有其他线程从队列中取走元素,有空位再入队。

当队列空的时候,继续出队列也会阻塞,直到有其他线程往队列中插入元素,有元素再出队。

应用场景

消息队列

作用:

1.解耦:高内聚,低耦合

2.削峰填谷

3.异步操作

1.JDK提供的阻塞队列

java 复制代码
public class Demo_01 {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<Integer> q = new ArrayBlockingQueue<>(3);
        q.put(1);
        System.out.println("添加了1");
        q.put(2);
        System.out.println("添加了2");
        q.put(3);
        System.out.println("添加了3");
        q.put(4);
        System.out.println("添加了4");
    }
}

添加三个元素之后进行阻塞

java 复制代码
public class Demo_01 {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<Integer> q = new ArrayBlockingQueue<>(3);
        q.put(1);
        System.out.println("添加了1");
        q.put(2);
        System.out.println("添加了2");
        q.put(3);
        System.out.println("添加了3");
        System.out.println(q.take());
        System.out.println(q.take());
        System.out.println(q.take());
        System.out.println(q.take());
    }
}

出队三个元素之后也进入了阻塞

2.自己实现阻塞队列

java 复制代码
public class MyBlockingQueue {
    // 定义一个数组来存放数据,具体的容量由构造方法中的参数决定
    private Integer[] elementData;
    // 定义头尾下标
    private volatile int head = 0;
    private volatile int tail = 0;
    // 定义数组中元素的个数
    private volatile int size = 0;
    public MyBlockingQueue(int capacity) {
        if(capacity <= 0) {
            throw new IllegalArgumentException("参数不合法");
        }
        elementData = new Integer[capacity];
    }
    public synchronized void put(Integer x) {
        //判断是否有位置插入,必须用while防止假唤醒
        while (size >= elementData.length) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        elementData[head++] = x;
        head = head % elementData.length;
        size++;
        this.notifyAll();
    }
    public synchronized Integer take() {
        //判断是否有元素可以取
        while (size == 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        Integer ans = elementData[tail++];
        tail = tail % elementData.length;
        size--;
        this.notifyAll();
        return ans;
    }
}

测试:

java 复制代码
public class Demo_02 {
    public static void main(String[] args) {
        MyBlockingQueue myBlockingQueue = new MyBlockingQueue(3);
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                myBlockingQueue.put(i);
                System.out.println("添加了元素: " + i);
            }
        });
        Thread t2 = new Thread(() -> {
            while (true) {
                System.out.println("取出: " + myBlockingQueue.take());
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t1.start();
        t2.start();
    }
}
相关推荐
serendipity_hky12 分钟前
互联网大厂Java面试故事:核心技术栈与场景化业务问题实战解析
java·spring boot·redis·elasticsearch·微服务·消息队列·内容社区
我真不会起名字啊12 分钟前
C、C++中的sprintf和stringstream的使用
java·c语言·c++
十点摆码13 分钟前
Spring Boot2 使用 Flyway 管理数据库版本
java·flyway·数据库脚本·springboo2·数据库脚本自动管理
毕设源码-钟学长31 分钟前
【开题答辩全过程】以 基于Javaweb的电动汽车充电桩管理系统为例,包含答辩的问题和答案
java·spring boot
多敲代码防脱发36 分钟前
为何引入Spring-cloud以及远程调用(RestTemplate)
java·开发语言
毕设源码-邱学长36 分钟前
【开题答辩全过程】以 基于JavaWeb的家庭理财管理系统的设计与实现为例,包含答辩的问题和答案
java
plmm烟酒僧42 分钟前
TensorRT 推理 YOLO Demo 分享 (Python)
开发语言·python·yolo·tensorrt·runtime·推理
sailing-data1 小时前
【SE】接口标准化
java·开发语言
t***p9351 小时前
idea创建springBoot的五种方式
java
+VX:Fegn08951 小时前
计算机毕业设计|基于springboot+vue的学校课程管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计