单例模式&阻塞队列详解

单例模式

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

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

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();
    }
}
相关推荐
怒放吧德德3 小时前
Netty 4.2 入门指南:从概念到第一个程序
java·后端·netty
雨中飘荡的记忆4 小时前
大流量下库存扣减的数据库瓶颈:Redis分片缓存解决方案
java·redis·后端
心之语歌7 小时前
基于注解+拦截器的API动态路由实现方案
java·后端
华仔啊8 小时前
Stream 代码越写越难看?JDFrame 让 Java 逻辑回归优雅
java·后端
ray_liang8 小时前
用六边形架构与整洁架构对比是伪命题?
java·架构
Ray Liang9 小时前
用六边形架构与整洁架构对比是伪命题?
java·python·c#·架构设计
Java水解10 小时前
Java 中间件:Dubbo 服务降级(Mock 机制)
java·后端
SimonKing14 小时前
OpenCode AI辅助编程,不一样的编程思路,不写一行代码
java·后端·程序员
FastBean14 小时前
Jackson View Extension Spring Boot Starter
java·后端
Seven9715 小时前
剑指offer-79、最⻓不含重复字符的⼦字符串
java