单例模式&阻塞队列详解

单例模式

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

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

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();
    }
}
相关推荐
踩着两条虫2 小时前
「AI + 低代码」的可视化设计器
开发语言·前端·低代码·设计模式·架构
JoneBB2 小时前
ABAP Webservice连接
运维·开发语言·数据库·学习
budingxiaomoli2 小时前
Spring IoC &DI
java·spring·ioc·di
Spider Cat 蜘蛛猫2 小时前
Springboot SSO系统设计文档
java·spring boot·后端
未若君雅裁2 小时前
MySQL高可用与扩展-主从复制读写分离分库分表
java·数据库·mysql
即使再小的船也能远航2 小时前
【Python】安装
开发语言·python
学习中.........2 小时前
从扰动函数的变化,感受红黑树带来的性能提升
java
Irissgwe2 小时前
类与对象(三)
开发语言·c++·类和对象·友元
计算机安禾3 小时前
【c++面向对象编程】第24篇:类型转换运算符:自定义隐式转换与explicit
java·c++·算法
雪度娃娃3 小时前
转向现代C++——优先选用nullptr而不是0和NULL
开发语言·c++