单例模式&阻塞队列详解

单例模式

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

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

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();
    }
}
相关推荐
咩咩啃树皮9 小时前
第40篇:Vue3组件化开发精讲——组件拆分、复用、父子通信、工程化架构
java·前端·架构
灯澜忆梦9 小时前
GO_并发编程---定时器
开发语言·后端·golang
鱟鲥鳚9 小时前
Spring Boot 集成 LangChain4j:从模型调用到 Tool Calling(Demo版)
java·spring boot
-银雾鸢尾-10 小时前
C#中的StringBuilder相关方法
开发语言·c#
-银雾鸢尾-10 小时前
C#中结构体与类的区别;抽象类与接口的区别
开发语言·c#
大模型码小白11 小时前
【Python零基础教程】继承、多态与魔法函数:面向对象编程三大核心特性详解
java·大数据·开发语言·人工智能·python·ai编程
腾渊信息科技公司12 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
爱笑的源码基地13 小时前
高并发 Redis 缓存门诊HIS系统源码,含财务统计药房进销存
java·程序·门诊系统·诊所系统·云诊所源码
wuqingshun31415913 小时前
TCP超时重传机制是为了解决什么问题?
java
段一凡-华北理工大学13 小时前
向量数据库实战:选型、调优与落地~系列文章12:文本分块策略实战:chunk_size 怎么选?重叠多少?
开发语言·数据库·后端·oracle·rust·工业智能体·高炉智能化