Java设计模式之单例模式

  一、前言:

  转载需要注明出处: https://i.cnblogs.com/EditPosts.aspx?opt=1

  单例模式其实很早之前就已经接触过了,但都是为了应付面试或者是为了装X。每过一段时间后,又有些模糊不清了,也仿佛从来没有项目中使用过,但最近终于有它的用武之地了。

  二、单例模式的特点:

    • 单例类只能有一个实例
    • 单例类自己给自己创建一个实例,注意是唯一的一个实例
    • 单例类必须给其他对象提供这唯一的实例

  三、单例模式分类:

    • 懒汉模式

      懒汉模式即懒加载,第一次被调用前不实例化。对于创建实例代价大,且不一定会使用时,使用懒加载模式可以减少开销

    • 饿汉模式

      饿汉模式,在类加载的同时实例化对象

  四、实现单例模式的各种方法:

    • 线程不安全的懒汉模式
public  class LazySingleton {
     private LazySingleton() {

    }
    private  static LazySingleton single;
     public  static LazySingleton getSinton(){
         if (single == null ){
            single = new LazySingleton();
        }
        return single;
    }
}

    这种方法在单线程场景中,是安全的,但在多线程情况中,单例模式未必是单例模式,也就是存在线程安全问题。

    • 同步方法下的多线程
public class LazySingleton {
    private LazySingleton() {

    }
    private static LazySingleton single;
    public static synchronized LazySingleton getSinton(){
        if(single == null){
            single = new LazySingleton();
        }
        System.out.println(Thread.currentThread().getName()+": "+single);
        return single;
    }
}

    使用sychronized修饰获取实例对象的方法,这种方法可以保证线程安全,但效率不高,每个线程都需要来获取 对象锁。

    • 同步代码块的懒汉模式
public class LazySingleton {
    private LazySingleton() {

    }

    private static LazySingleton single;

    public static LazySingleton getSinton() {
        if (single == null) {
            synchronized (LazySingleton.class) {
                single = new LazySingleton();
            }
        }
        System.out.println(Thread.currentThread().getName() + ": " + single);
        return single;
        
    }
}

    由同步方法想到同步代码块,但实际上,这种方法并不是线程安全。

    解释:两个线程A、B,A线程判断single是空的 ,当获取到锁后;B线程开始判断,发现single是空的;这时A线程又快人一步,创建了一个对象;线程B来了,获取到锁,也创建了一个对象。

    • 双重检查的懒汉  
public class LazySingleton {
    private LazySingleton() {

    }

    private static LazySingleton single;

    public static LazySingleton getSinton() {
        if (single == null) {
            synchronized (LazySingleton.class) {
                if(single== null){
                    single = new LazySingleton();
                }
            }
        }
        System.out.println(Thread.currentThread().getName() + ": " + single);
        return single;
        
    }
}

    针对同步代码块的懒汉,双重检查的懒汉可以说是最佳的,既保证了线程安全,又不需要每次都获取锁资源。(推荐双重检查懒汉)

    • 静态常量饿汉
public class HungrySingleTon {
    
    private HungrySingleTon(){
    }
    
    private final static HungrySingleTon single = new HungrySingleTon();
    
    public static HungrySingleTon getInstance(){
        return single;
    }
}

    无线程安全问题,缺点是类装在完成实例化,若一直未使用,会造成资源浪费。

    • 静态代码块饿汉模式
public class HungrySingleTon {

    private HungrySingleTon() {

    }

    private static HungrySingleTon single;

    static {
        single = new HungrySingleTon();
    }

    public static HungrySingleTon getInstance() {
        return single;
    }
}

      线程安全,与常量饿汉模式一样,若一直未被使用,会造成资源浪费。

    • 静态内部类
public class HungrySingleTon {

    private HungrySingleTon() {

    }

    public  static HungrySingleTon getInstance() {
         return InnerClass.single;
    }

    private  static  class InnerClass {
         private  static  final HungrySingleTon single = new HungrySingleTon();
    }
}

    只有调用getInstance()时才会加载静态内部类。无线程安全问题,也实现了懒加载。

  

  

  

  

原文地址:https://www.cnblogs.com/lfdingye/p/7569358.html