单例模式

2021-05-05  本文已影响0人  金煜博

什么是单例模式?

保证jvm中只能有一个实例

1.饿汉式

提前创建对象,保证了线程的安全,占内存空间,影响程序启动效率

示例代码

public class Singleton {
    public  static Singleton singleton = new Singleton();
    private Singleton() {
    }
    public static Singleton getInitial(){
        return singleton;
    }
}
public class V1 {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getInitial();
        Singleton singleton2 = Singleton.getInitial();
        System.out.println(singleton1==singleton2);
    }
}

2.懒汉式

需要用到时才创建对象实例,因为在方法上加了锁,导致创建和获取对象都上了锁,效率低。

示例代码

public class Singleton {
    private static Singleton singleton;
    //私有的构造方法可以禁止new对象
    private Singleton() {

    }

     //在方法上加锁导致读写操作都上锁了效率低,成了单线程了
     public static synchronized Singleton  getInitial(){
         try {
             Thread.sleep(3000);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
        if(singleton==null){
            singleton = new Singleton();
        }
        return singleton;
     }
}
public class V2 {
   //创建100个线程模拟执行效率和线程是否安全
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Singleton singleton1 =  Singleton.getInitial();
                    System.out.println(Thread.currentThread().getName()+"-----------"+singleton1);
                }
            }).start();
        }
    }
}

3.双重检验锁

双重检验锁解决了在写上锁,读不上锁,有效的提高执行效率
(重点理解下两个if判断的意义)

示例代码

public class Singleton {
    private static Singleton singleton;
    //私有的构造方法可以禁止new对象
    private Singleton() {

    } 
    //在代码段上加锁,只是在创建Singleton时上锁,读取时无锁。(双重检验锁)
    public static Singleton getInitial() {
        //第一个if判断singleton是否null不是就可以直接读取singleton对象
        if (singleton == null) {
            synchronized (Singleton.class) {
                //第二个if判断防止多个线程同时拿到锁后,重复创建singleton对象
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
public class V2 {
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Singleton singleton1 =  Singleton.getInitial();
                    System.out.println(Thread.currentThread().getName()+"-----------"+singleton1);
                }
            }).start();
        }
    }
}

4.静态类部内

静态内部内结合了懒汉式和饿汉式的优,效率高

示例代码

public class Singleton {
    private Singleton() {
        System.out.println("-----------初始化构造函数-------");
    }

    private static  Singleton initialSingleton(){
        return GetSingleton.singleton;
    }

    private static class GetSingleton{
        private  final  static  Singleton singleton =  new Singleton();
    }

    public static void main(String[] args) {
        System.out.println("开始运行");
        Singleton singleton1 =  Singleton.initialSingleton();
        Singleton singleton2 =  Singleton.initialSingleton();
        System.out.println(singleton1==singleton2);
    }

5.枚举

使用枚举方式可以有效的防止反射机制和序列化,因为jdk底层源码中有if判断禁用了反射与序列化

public enum  Singleton {
    SL;
    public   void show(){
        System.out.println("枚举单例模式可以防止反射和序列化");
    }
}
public class V4 {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.SL;
        Singleton singleton2 = Singleton.SL;
        System.out.println(singleton1 == singleton2);
        singleton1.show();
    }
}

6.单例模式额外注意点

1.使用反射机制可以破解单例
2.使用序列化可以破解单例
3.可以在无参构造方法中判断单例对象是否为null,是就抛出异常
4.不用懒加载的话用饿汉式,反之用静态类部内方式,需要序列化的对象用枚举方式

上一篇下一篇

猜你喜欢

热点阅读