目录

多线程单例模型阻塞队列

多线程——单例模型、阻塞队列


一、单例模型

单例模式,是 设计模式 (针对问题使用不同方式解决问题)中一种非常经典的模式,而单例模式就如其名字一样,强制要求 一个类不能创建多个对象 。

而单例模型大概分俩种,一种是 饿汉模式 ,一种是 懒汉模式 。

1、饿汉模式

饿汉模式,听名字就知道是非常饥饿,而在java中的意思就是在

类加载的同时,创建实例 。

class Singleton{
    private static Singleton instance=new Singleton(100);

    public static Singleton getInstance() {
        return instance;
    }

    private Singleton(){

    }

    private Singleton(int n){

    }
}

而在过程中,只能返回一个实例,不能返回多个,不然会报错,这一点在很多场景上都需要. 比如 JDBC 中的 DataSource 实例就只需要一个。

2、懒汉模式

而懒汉模式和饿汉模式恰恰相反,懒汉模式是

使用的时候再创建实例 ,不使用不创建实例。同时,懒汉模式也有分为单线程和多线程类。单线程如下:

class SingletonLazy{
    private static SingletonLazy instance=null;
   
    public static SingletonLazy getInstance2(){
        if(instance==null){
            instance=new SingletonLazy();
        }
        return instance;
    }

    private  SingletonLazy(){

    }
}

在上述中懒汉模式是需要用的时候再创建实例,不需要就不用,对于单线程来说这个是可以的,但对于多线程中的话,因为多线程不是 原子性 的,所以在同时调用getInstance2方法的话,就很有可能会导致创建多个实例,所以,为了杜绝这个问题,我们需要加上 synchronized 来改善这里的线程安全问题。

class SingletonLazy{
    private static volatile SingletonLazy instance=null;
    private static  Object locker=new Object();

    public static  SingletonLazy getInstance(){
        if(instance==null){
            synchronized (locker){
                if (instance==null){
                    instance=new SingletonLazy();
                }
            }
        }
        return instance;
    }

    private  SingletonLazy(){

    }
}

用双重if来降低锁竞争,然后再用voliatile解决可见性问题,这样的话就能解决线程安全问题。

二、阻塞队列

1、阻塞队列原理

阻塞队列是什么?阻塞队列其实就是一种特殊的队列,遵守 “先进先出” 的原则。

我们以图为例子:

https://i-blog.csdnimg.cn/direct/20982e6a114c43968ff2401ee344df55.png

这是一个很普通的客户端A向服务器B发起请求,如果没有阻塞队列的话,那么就直接全部数据一起传输,如果有阻塞的话,就会变成下面情况:

https://i-blog.csdnimg.cn/direct/01dbc0b9becb4c01b06dce514a70aae8.png

在客户端A向客户端B发出请求的时候,中间有阻塞队列, 如果里面队满(阻塞队列存储满),而不能够入队列,直到队列不满。如果队列为空,则不能够出队列,直到队列不为空。

2、阻塞队列常见用法

阻塞队列最常用的就是put和take方法, put方法就是将元素添加到阻塞队列,而take则是拿出来 ,有一点十分注意的是,阻塞队列中有offer的方法,但使用这个是不会被阻塞的,就会变成一个普通的队列。

 public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> queue=new LinkedBlockingQueue<>(10);
        for (int i = 0; i < 10; i++) {
            queue.put("aaa");
        }
        System.out.println("队列已满");

        for (int i = 0; i < 10; i++) {
            System.out.println(queue.take());
        }
        System.out.println("队列为空");
        queue.take();
    }

https://i-blog.csdnimg.cn/direct/e65b46cc6293463eb5bd63c940413c12.png

3、生产者消费者模型

而我们对于阻塞队列中有关的的有生产者消费者模型,这个案例十分典型,解释一下什么是生产者消费者模型,在一个阻塞队列中,生产速度大于消费速度,那么当

生产量和消费量的差值已经是阻塞队列的大小之后,那么就会进行阻塞 ,直到保证生产量和消费量的差值比阻塞队列的大小还小,这样就能做到生产者和消费者之间的硬性平衡。以下面为案例:

  public static void main(String[] args) {
        BlockingQueue<Integer> queue=new LinkedBlockingQueue<>(4);
        Thread producer=new Thread(()->{
            int n=0;
            while (true){
                try {
                    queue.put(n);//offer不会阻塞,put才行
                    System.out.println("生产元素"+n);
                    n++;
                    Thread.sleep(1000);
                }catch(InterruptedException e){
                    throw new RuntimeException(e);
                }
            }
        },"producer");

        Thread consumer=new Thread(()->{
            while(true){
                try{
                    Integer n=queue.take();
                    System.out.println("消费元素"+n);

                    Thread.sleep(3000);
                }catch(InterruptedException e){
                    throw new RuntimeException(e);
                }
            }
        },"consumer");
        producer.start();
        consumer.start();
    }

在上面图中,生产的速度是一秒一个,而消费的速度是三秒一个,但阻塞队列的大小为4,所以当生产元素减去消费元素>=4的时候,就会进行阻塞,直到差值小于4.

https://i-blog.csdnimg.cn/direct/da99c0a9e6584220a1fe46821057c9a1.png