当前位置:网站首页>【手撕代码】单例模式及生产者/消费者模式

【手撕代码】单例模式及生产者/消费者模式

2022-07-06 09:20:00 李孛欢

单例模式

 单例模式(Singleton Pattern):保证一个类仅有一个对象,并提供一个访问它的全局访问点。

单例模式的优点:

  • 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动 时直接产生一个单例对象,然后永久驻留内存的方式来解决
  • 单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计 一个单例类,负责所有数据表的映射处理 

代码实现:

饿汉式 :先创建后使用,线程安全,占用内存。

public class SingleClassA {

    //1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象
    private SingleClassA(){ }
    //2.在类的内部创建一个类的实例
    //类初始化时,立即加载这个对象(没有延时加载的优势)。加载类时,天然的是线程安全的!
    private static final SingleClassA instance = new SingleClassA();
    //3.对外部提供调用方法:将创建的对象返回,只能通过类来调用
    //方法没有同步,调用效率高!
    public static SingleClassA  getInstance(){
        return instance;
    }

    public static void main(String[] args) {
        SingleClassA a = getInstance();
        SingleClassA b = getInstance();
        System.out.println(a == b);
    }
}

输出结果:

 懒汉式:用的时候才创建,线程不安全(创建或获取单例对象时),加锁会影响效率。

public class SingleClassB {

        //1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象
        private SingleClassB(){ }
        //2.在类的内部创建一个类的实例
        private static SingleClassB instance ;
        //3.对外部提供调用方法:将创建的对象返回,只能通过类来调用
        public static synchronized SingleClassB  getInstance(){
            if(instance == null) {
                instance = new SingleClassB();
            }
            return instance;
        }

        //测试
        public static void main(String[] args) {
            SingleClassB a = SingleClassB.getInstance();
            SingleClassB b = SingleClassB.getInstance();
            System.out.println(a==b);
        }
    }

输出结果:

懒汉模式和饿汉模式的区别:

  1. 懒汉式默认不会实例化,要等到外部调用方法时才会,饿汉式类加载阶段就实例化了对象
  2. 线程安全上,饿汉式肯定是线程安全的,因为在线程没出现之前就实例化了对象,懒汉式则是线程不安全的,因为在多线程下,如果一个线程判断完实例为null就休眠或着中断,那么另一个线程也进入方法,判断实例也为null,那么该线程就会创建实例对象,而原来的那个休眠线程恢复以后,直接就执行实例化new对象这一步,那么就会出现多个实例。
  3. 分析上面的线程安全,接下来就是性能了,可能你已经想到了,饿汉式不需要加锁,执行效率高,懒汉式需要加锁,执行效率低
  4. 占用内存上,饿汉式不管你用不用到它的实例对象,他一开始就已经实例化在那里了,占据了内存空间,而懒汉式等到用的时候才实例化,不会浪费内存

生产者/消费者模式

生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。如下图所示:

 代码实现:

@Slf4j(topic = "ProductAndConsumer")
public class ProductAndConsumer {
    public static void main(String[] args) {
        MessageQueue messageQueue = new MessageQueue(2);
// 3 个生产者线程
        for (int i = 0; i < 3; i++) {
            int id = i;
            new Thread(() -> {
                messageQueue.put(new Message(id, "值" + id));
            }, "生产者" + i).start();
        }
// 1 个消费者线程, 处理结果
        new Thread(() -> {
            while (true) {
                try {
                    sleep(1000);
                    Message message = messageQueue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }, "消费者").start();
    }
}


final class Message {
    private int id;
    private Object message;

    public Message(int id, Object message) {
        this.id = id;
        this.message = message;
    }
    public int getId() {
        return id;
    }
    public Object getMessage() {
        return message;
    }

    @Override
    public String toString() {
        return "Message{" +
                "id=" + id +
                ", message=" + message +
                '}';
    }
}

@Slf4j(topic = "MessageQueue")
class MessageQueue {
    private LinkedList<Message> queue;
    private int capacity;

    public MessageQueue(int capacity) {
        this.capacity = capacity;
        queue = new LinkedList<>();
    }

    public Message take() {
        synchronized (queue) {
            while (queue.isEmpty()) {
                log.debug("队列为空,消费者线程等待");
                try {
                    queue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            Message message = queue.removeFirst();
            log.debug("已消费消息{}",message);
            queue.notifyAll();
            return message;
        }
    }

    public void put(Message message) {
        synchronized (queue) {
            while (queue.size() == capacity) {
                try {
                    log.debug("库存已达上限, 生产者线程等待");
                    queue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            queue.addLast(message);
            log.debug("已生产消息{}",message);
            queue.notifyAll();
        }
    }
}

输出结果如下:

原网站

版权声明
本文为[李孛欢]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_61543601/article/details/125164762