设计模式——1.单例模式

• 创建型模式:
– 单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。
• 结构型模式:
– 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模 式。
• 行为型模式:
– 模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模 式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。

就是由软件界大佬GOF总结出的一些java写代码的时候的固定套路。不要背,去用,分析语句好处。

1.单例模式
一个类只有一个实例对象,提供一个public方法获取这个对象。
应用场景:
在这里插入图片描述
创建一个对象需要比较多的资源时,比如读取配置文件,产生其他依赖对象,就用单例模式,应用启动的时候创建一次,永久保存在内存中。
在这里插入图片描述
1)饿汉式

package com.bjsxt.singleton;

import java.io.Serializable;

/**
 * 饿汉模式
 */
public class SingletonDemo1 implements Serializable {
    // 没有延迟加载 线程安全
    private static SingletonDemo1 instance = new SingletonDemo1();
    // 构造器私有 一个类只有一个对象
    private SingletonDemo1(){}
    public static SingletonDemo1 getInstance(){
        return instance;
    }
}

饿汉式单例模式代码中,static变量会在类装载时初始化,此时也不会涉及多个线程对象访问该对象的问 题。虚拟机保证只会装载一次该类,肯定不会发生并发访问的问题。因此,可以省略synchronized关键字。

问题:如果只是加载本类,而不是要调用getInstance(),甚至永远没有调用,则会造成资源浪费!
2)懒汉式

package com.bjsxt.singleton;

public class SingletonDemo2 {
    private static SingletonDemo2 instance;
    private SingletonDemo2(){
    }
    // 线c安全 延迟加载 调用效率低
    public synchronized static SingletonDemo2 getInstance() {
        if(instance==null){
            instance=new SingletonDemo2();
        }
        return instance;
    }
}

lazy load! 延迟加载, 懒加载! 真正用的时候才加载!

问题: – 资源利用率高了。但是,每次调用getInstance()方法都要同步,并发 效率较低。
3)双重检测锁实现
在这里插入图片描述
4)静态内部类实现方式(也是一种懒加载方式)

package com.bjsxt.singleton;

public class SingletonDemo3 {
    private static class SingletonDemo3Inline{
        private static SingletonDemo3 instance = new SingletonDemo3();
    }
    private SingletonDemo3(){}
    public static SingletonDemo3 getInstance(){
        return SingletonDemo3Inline.instance;
    }
}

外部类没有static属性,则不会像饿汉式那样立即加载对象。
– 只有真正调用getInstance(),才会加载静态内部类。加载类时是线程 安全的。 instance是static final 类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性.
– 兼备了并发高效调用和延迟加载的优势

• 问题:
反射设置access为true可以破坏一个类只有一个对象(不包含枚举式)!反序列化(不包含枚举式)将对象写入文件之后读取文件到对象会生成新的对象!也违背了一个类只有一个对象!

package com.bjsxt.singleton;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;

public class Client2 {
    public static void main(String[] args) throws Exception {
        // 反射破坏饿汉模式 一个类不只有一个对象
        Class<SingletonDemo1> singletonDemo1Class= (Class<SingletonDemo1>) Class.forName("com.bjsxt.singleton.SingletonDemo1");
        Constructor<SingletonDemo1> constructor = singletonDemo1Class.getDeclaredConstructor(null);
        constructor.setAccessible(true);
        SingletonDemo1 s1=constructor.newInstance();
        SingletonDemo1 s2=constructor.newInstance();
        System.out.println(s1);
        System.out.println(s2);
        // 反射破坏懒汉模式 一个类不只有一个对象
        Class<SingletonDemo2> singletonDemo2Class2= (Class<SingletonDemo2>) Class.forName("com.bjsxt.singleton.SingletonDemo2");
        Constructor<SingletonDemo2> constructor2 = singletonDemo2Class2.getDeclaredConstructor(null);
        constructor2.setAccessible(true);
        SingletonDemo2 s3=constructor2.newInstance();
        SingletonDemo2 s4=constructor2.newInstance();
        System.out.println(s3);
        System.out.println(s4);
        // 反射破坏静态内部类模式
        Class<SingletonDemo3> singletonDemo3Class3= (Class<SingletonDemo3>) Class.forName("com.bjsxt.singleton.SingletonDemo3");
        Constructor<SingletonDemo3> constructor3 = singletonDemo3Class3.getDeclaredConstructor(null);
        constructor3.setAccessible(true);
        SingletonDemo3 s5=constructor3.newInstance();
        SingletonDemo3 s6=constructor3.newInstance();
        System.out.println(s5);
        System.out.println(s6);
        // 反射破坏枚举方式 无法破坏!
        /*Class<SingletonDemo4> singletonDemo4Class= (Class<SingletonDemo4>) Class.forName("com.bjsxt.singleton.SingletonDemo4");
        Constructor<SingletonDemo4> constructor4 = singletonDemo4Class.getDeclaredConstructor(null);
        constructor4.setAccessible(true);
        SingletonDemo4 s7=constructor4.newInstance();
        SingletonDemo4 s8=constructor4.newInstance();
        System.out.println(s7);
        System.out.println(s8);*/

        // 反序列化破坏单例模式 一个类不只有一个对象
        FileOutputStream fos=new FileOutputStream("d:/a.txt");
        ObjectOutputStream oos=new ObjectOutputStream(fos);
        oos.writeObject(s1);
        oos.close();
        fos.close();

        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("d:/a.txt"));
        SingletonDemo1 s10 = (SingletonDemo1) ois.readObject();
        System.out.println(s1);
        System.out.println(s10);
    }
}

运行结果:
在这里插入图片描述
解决办法:
在这里插入图片描述
5)枚举

package com.bjsxt.singleton;

public enum SingletonDemo4 {
    INSTANCE;
    public void operateInstance(){

    }
}

优点:
– 实现简单
– 枚举本身就是单例模式。由JVM从根本上提供保障!避免通过反射和反序列化的漏洞!
• 缺点:
– 无延迟加载

2 常见的五种单例模式在多线程环境下的效率测试

CountDownLatch

– 同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一 个或多个线程一直等待。
countDown() 当前线程调此方法,则计数减一(建议放在 finally里执行)
await(), 调用此方法会一直阻塞当前线程,直到计时器的值为0

package com.bjsxt.singleton;

import java.util.concurrent.CountDownLatch;

public class Client3 {
    public static void main(String[] args) throws InterruptedException {
        for(int i=0;i<3;i++){
            test1();
            test2();
            test3();
            test4();
            System.out.println("-------");
        }

    }
    public  static void test1(){
        int threadNum=10;
        CountDownLatch countDownLatch=new CountDownLatch(threadNum);
        long start=System.currentTimeMillis();
        for(int i=0;i<threadNum;i++){
            new Thread(()->{
                for(int j=0;j<1000000;j++){
                    SingletonDemo1 instance = SingletonDemo1.getInstance();
                }
                countDownLatch.countDown();
            }).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end=System.currentTimeMillis();
        System.out.println("单例模式:饿汉模式模式"+(end -start)+" ms");
    }
    public  static void test2(){
        int threadNum=10;
        CountDownLatch countDownLatch=new CountDownLatch(threadNum);
        long start=System.currentTimeMillis();
        for(int i=0;i<threadNum;i++){
            new Thread(()->{
                for(int j=0;j<1000000;j++){
                    SingletonDemo2 instance = SingletonDemo2.getInstance();
                }
                countDownLatch.countDown();
            }).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end=System.currentTimeMillis();
        System.out.println("单例模式:懒汉模式"+(end -start)+" ms");
    }
    public  static void test3(){
        int threadNum=10;
        CountDownLatch countDownLatch=new CountDownLatch(threadNum);
        long start=System.currentTimeMillis();
        for(int i=0;i<threadNum;i++){
            new Thread(()->{
                for(int j=0;j<1000000;j++){
                    SingletonDemo3 instance = SingletonDemo3.getInstance();
                }
                countDownLatch.countDown();
            }).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end=System.currentTimeMillis();
        System.out.println("单例模式:静态内部类模式"+(end -start)+" ms");
    }
    public  static void test4(){
        int threadNum=10;
        CountDownLatch countDownLatch=new CountDownLatch(threadNum);
        long start=System.currentTimeMillis();
        for(int i=0;i<threadNum;i++){
            new Thread(()->{
                for(int j=0;j<1000000;j++){
                    SingletonDemo4 instance = SingletonDemo4.INSTANCE;
                }
                countDownLatch.countDown();
            }).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end=System.currentTimeMillis();
        System.out.println("单例模式:枚举"+(end -start)+" ms");
    }
}

运行结果
在这里插入图片描述
使用uml看类图
在这里插入图片描述

3 总结:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值