单例模式(详细)

B站学习做的笔记

单例模式:

单例模式就是采取一定的方法保证整个软件
统里面对于某个类只能存在一个实例  
并且该类只提供一个取对象实例的方法(静态方法)

单例模式的写法

  1. 饿汉式(静态常量)

  2. 饿汉式(静态代码块)

  3. 懒汉式(线程不安全)

  4. 懒汉式(线程安全 同步方法)

  5. 双重检查

  6. 静态内部类

  7. 枚举

饿汉式的两种写法

1静态常量

    步骤
   1. 构造器私有化(防止new )
   2. 类的内部创建对象
   3. 向类的外面暴露一个静态的公共方法getInstance
   4. 代码实现
package com.设计模式.单例模式;

public class singleton1 {
    public static void main(String[] args) {

     Singleton singleton  = Singleton.getInstance();
     Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton.hashCode());
        System.out.println(singleton1.hashCode());
    }
}
//饿汉式(静态常量)
class Singleton{
    //构造器私有化
    private Singleton() {

    }
    //内部创建实例
    private final static  Singleton singleton = new Singleton();
    //提供返回方法
    public  static  Singleton getInstance(){
        return singleton;
    }

}
  325040804
  325040804
  hascode相同

优缺点

  1. 写法简单 在类加载的时候完成实例化。避免线程同步的问题
  2. 缺点:在类加载的时候完成实例化 没有达到 lazy loading 的效果 如果从来没有使用这个实例 就会造成内存的浪费
  3. 这种方法基于classloder 机制避免了多线程 的同步问题 在instance 在类加载时候就实例化 在单例中大多是调用getInstance方法 但是导致类装置的原因有很多 不能确定有其他的方式导致类装载 这时候初始化instance 没有达到lazy loading的效果

4 结论:这种方法可能会导致内存浪费.

静态代码块

package com.设计模式.单例模式;

public class Test {
    public static void main(String[] args) {
    Singleton2 singleton2 = Singleton2.getInstance();
    Singleton2 singleton3 = Singleton2.getInstance();
        System.out.println(singleton2.hashCode()+"----"+singleton3.hashCode());
    }
}

class Singleton2{
    private static  Singleton2 singleton2;

    private Singleton2() {
    }

    static {
        singleton2 = new Singleton2();
    }
    public  static  Singleton2 getInstance(){
        return singleton2;
    }

}

优缺点 同上

懒汉式

1 线程不安全

package com.设计模式.单例模式;

public class Test2 {
    public static void main(String[] args) {
        SingletonTest test = SingletonTest.getInstance();
        SingletonTest test2 = SingletonTest.getInstance();
        System.out.println(test.hashCode()+"__"+test2.hashCode());
    }
}
class  SingletonTest{
    private static  SingletonTest Instance;
    private  SingletonTest(){ }
    //提供一个静态方法 用到方法再去创建instance
    public  static  SingletonTest getInstance(){
        if (Instance ==null){
            Instance = new SingletonTest();
        }
        return Instance;
    }
}

优缺点

 1.起到了lazy loading 的效但是只能在单线程下使用
 
2 多线程下 一个线程进入if 判断  还未来得及向下执行  另一个线程也进去判断 这样会产生多个实例 (多线程下不可使用)

2 线程安全

public class Test {
    public static void main(String[] args) {
        Singleton instacne = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();
        System.out.println(instacne == singleton2);
    }
}
class Singleton{

    private  static Singleton instance;
    private Singleton(){}
        //加入同步处理
    public static synchronized Singleton getInstance(){
        if (instance==null){
            instance = new Singleton();
        }
     return  instance;
    }
}

问题:

1 解决了线程不安全的问题

2 效率低了 线程要获得实例的时候getInstance 都要同步

注: 如果在这使用同步代码块 起不到线程安全的作用

双重检查

public class test {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton == singleton1);
    }

    }
class  Singleton{
        private  static volatile Singleton singleton;
        private  Singleton(){}
        public static  Singleton getInstance(){

            if (singleton ==null){
                synchronized (Singleton.class){
                    if (singleton ==null){
                        singleton = new Singleton();
                    }
                }
            }
            return  singleton;
        }
}

//对象创建可能指令重排,创建空间,指向空间,然后才初始化,volatile可保证有序,可见

这里必须加volalite的原因:

 new 对象的过程分为三步:   
 
    1.分配空间
    2.初始化对象
    3.指向对象内存地址。2和3可能被编译器自动重排,导致判断非空但是实际拿的对象还未完成初始化
    
    
  注:  new 操作中初始化内存空间和指向 内存空间这两步操
    作会根据情况不同,执行顺序不同,如果先
    是指向内存空间执行,这时对
    象不为空,另一个线程有
    可能进入并返回未初始化的对象

优缺点

1 Double-check 保证线程的安全性
2 线程安全 延迟加载 效率较高

静态内部类

public class Test {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton == singleton1);


    }
}

class Singleton{

    private  Singleton(){}
    private  static  class  singletonInstance{
        private static final  Singleton INSTANCE = new Singleton();
    }
    public static   Singleton getInstance(){
        return singletonInstance.INSTANCE;
    }
}

1 使用了类装载的机制保证初始化实例只有一个线程

2静态内部类在类被装载时候并不会立即实例化  而是在
需要实例化的时候调用getInstance()  才会装载内部类完成实例化
3 类的静态属性在第一次加载类的时候会初始化  jvm 
帮助我们保护l线程安全
4 利用静态内部类实现延迟加载 效率高

枚举

public class test {
    public static void main(String[] args) {
        Singleton singleton = Singleton.INSTANCE;
        Singleton singleton2 = Singleton.INSTANCE;
        System.out.println(singleton.hashCode()+"___"+singleton2.hashCode());
        singleton.HELLO();
    }
}
enum Singleton{
    INSTANCE;
    public void HELLO(){
        System.out.println("HEllO");
    }
}

避免多线程同步问题 还能防止反序列化重新创建新的对象

你可能感兴趣的:(设计模式)