单例模式&阻塞队列详解

单例模式

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

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

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();
    }
}
相关推荐
skiy几秒前
Spring boot创建时常用的依赖
java·spring boot·后端
早起的年轻人3 分钟前
告别Git仓库臃肿:一招解决Maven target目录误提交问题
java·git·maven
2401_846341654 分钟前
调试技巧与核心转储分析
开发语言·c++·算法
Rooting++7 分钟前
C 指针重点
c语言·开发语言
2301_815482937 分钟前
C++安全编程指南
开发语言·c++·算法
2401_851272997 分钟前
内存映射文件高级用法
开发语言·c++·算法
快乐柠檬不快乐8 分钟前
Java连接电科金仓数据库(KingbaseES)实战指南
java·开发语言·数据库
yunyun321239 分钟前
C++中的观察者模式变体
开发语言·c++·算法
小喻同学i9 分钟前
卸载VS2015,安装VS2017后Qt报错问题
开发语言·qt
程序员清风11 分钟前
看完Anthropic研究才懂:你有多会问,AI就有多强!
java·后端·面试