码迷,mamicode.com
首页 > 其他好文 > 详细

设计模式之单例模式

时间:2018-05-06 14:57:25      阅读:166      评论:0      收藏:0      [点我收藏+]

标签:lang   targe   分布   额外   method   阻塞   print   2.4.1   been   

1 饿汉模式

      1.1本质: 在类加载的时候就创建实例,需要获取实例时直接返回已创建的实例

      1.2 优点:线程安全

      1.3 缺点: 类加载的时候就创建实例,浪费空间

      1.4 代码示例:

package com.blueStarWei.pattern;

public class HungrySingleton {

    //create instance when loading class
    private static HungrySingleton singleton = new HungrySingleton();
    
    public static HungrySingleton getInstance(){
        return singleton;
    }
}

 

【为了减省空间的浪费,因此人们想到了在需要获取实例的时候创建实例,因此产生了懒汉模式】

 

2. 懒汉模式

  2.1 本质: 当需要获取实例的时候,先进行判断,如果实例不存在,就创建实例,否则直接返回已存在的实例【避免了在加载类的同时直接创建实例】

  2.2 懒汉模式1.0版本

package com.blueStarWei.pattern;

public class LazySingleton {

    private static LazySingleton singleton;
    
    public static LazySingleton getInstance(){
        if(singleton == null){
            singleton = new LazySingleton();
        }
        return singleton;
    }
}

    2.2.1 优点:节省了空间

    2.2.2 缺点: 线程不安全【如果多个线程同时执行if判断,因为多个线程获取的singleton1都是空,所以会创建多个实例】

 

【为了避免懒汉模式的线程不安全问题,人们想到了对getInstance()添加synchronized修饰符,于是产生了懒汉模式1.1版本】

 

  2.3 懒汉模式1.1版本

package com.blueStarWei.pattern;

public class LazySingleton1 {

    private static LazySingleton1 singleton;
    
    public static synchronized LazySingleton1 getInstance(){
        if(singleton == null){
            singleton = new LazySingleton1();
        }
        return singleton;
    }
}

    2.3.1 优点: 节省空间、线程安全

    2.3.2 缺点: 性能不好【synchronized会造成线程阻塞】

 

【为了避免线程阻塞的出现,人们又想到了双重校验锁,因此产生了懒汉模式1.2版本】

 

  2.4 懒汉模式1.2版本

package com.blueStarWei.pattern;

public class LazySingleton2 {

    private static LazySingleton2 singleton;
    
    public static LazySingleton2 getInstance(){
        if(singleton == null){
            synchronized (LazySingleton1.class) {
                if(singleton == null){
                    singleton = new LazySingleton2();                                    
                }
            }
        }
        return singleton;
    }
}

    2.4.1 优点: 节省空间,线程安全,性能相对有优化【只是优化了已经存在实例的情况,即:当实例已经被创建,再调用getInstance()的时候就不会经过synchronized修饰符】

    2.4.2 缺点:使用了synchronized修饰符,线程仍然会有阻塞情况的发生。

 

【为了避免使用synchronized修饰符,人们想到了静态内部类(因为一个类被加载,当且仅当该类的某个静态成员被调用),于是懒汉模式2.0时代到来】

 

  2.5 懒汉模式2.0版本【推荐版本

package com.blueStarWei.pattern;
/**
 * 因为对象实例化是在内部类加载时构建,因此是线程安全的【毕竟类只加载一次嘛】
 * @author HuWei
 *
 */
public class LazySingleton3 {
    
    //当getInstance()方法第一次被调用时,内部类被加载
    static class SingletonHolder {
        private static final LazySingleton3 singleton3 = new LazySingleton3();
    }
    
    //没有使用synchronized修饰符,避免了性能损耗
    public static LazySingleton3 getInstance() {
        return SingletonHolder.singleton3;
    }
}

     2.5.1 优点: 节省空间、线程安全、避免性能的额外损耗

     2.5.2 缺点: 在反射作用下,单例模式会被破坏

 

【为了防止反射破坏单例模式,所以产生了单例模式3.0版本】

 

  2.6 单例模式3.0版本

     2.6.1 版本代码

package com.blueStarWei.pattern;

public class LazySingleton4 {
    
    /* -------添加部分 start-------- 
     * 作用:第二次调用构造函数(即:创建第二个实例)时抛出异常
     * */
    private static boolean initialized = false;
    
    public LazySingleton4() {
        synchronized (LazySingleton4.class) {
            if(initialized == false){
                initialized = !initialized;
            }else{
                throw new RuntimeException("Singleton has been broken!");
            }
        }
    }
    /* -------添加部分 end-------- */
    
    //当getInstance()方法第一次被调用时,内部类被加载
        static class SingletonHolder {
            private static final LazySingleton4 singleton4 = new LazySingleton4();
        }
        
        //没有使用synchronized修饰符,避免了性能损耗
        public static LazySingleton4 getInstance() {
            return SingletonHolder.singleton4;
        }
}

        2.6.1.1  版本Demo

package com.blueStarWei.pattern;

import java.lang.reflect.Constructor;
/**
 * 通过反射调用时会报错
 * Caused by: java.lang.RuntimeException: Singleton has been broken!
 * @author HuWei
 *
 */
public class LazySingleton4Demo {

    public static void main(String[] args) {
        //通过getInstance()创建实例
        LazySingleton4 sington = LazySingleton4.getInstance();
        
        //通过反射创建实例
        LazySingleton4 sington4 = null;
        try {
            Class<LazySingleton4> clazz = LazySingleton4.class;
            Constructor<LazySingleton4> constructor = clazz.getDeclaredConstructor();
            sington4 = constructor.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        } 
        
        System.out.println("getInstance() : "+sington);
        System.out.println("invoke : "+sington4);
    }
}

         2.6.1.2 Demo日志

java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at com.blueStarWei.pattern.LazySingleton4Demo.main(LazySingleton4Demo.java:21)
Caused by: java.lang.RuntimeException: Singleton has been broken!
    at com.blueStarWei.pattern.LazySingleton4.<init>(LazySingleton4.java:19)
    ... 5 more
getInstance() : com.blueStarWei.pattern.LazySingleton4@7852e922
getInstance() : com.blueStarWei.pattern.LazySingleton4@7852e922
invoke : null

 

    2.6.2 反射破环单例模式示例

package com.blueStarWei.pattern;

import java.lang.reflect.Constructor;
/**
 * 不难发现,通过getInstance()和反射创建的实例的内存地址不一样,即不是同一个实例对象
 * @author HuWei
 *
 */
public class LazySingleton3Demo {

    public static void main(String[] args) {
        //通过getInstance()创建实例
        LazySingleton3 sington = LazySingleton3.getInstance();
        LazySingleton3 sington2 = LazySingleton3.getInstance();
        
        //通过反射创建实例
        LazySingleton3 sington3 = null;
        try {
            Class<LazySingleton3> clazz = LazySingleton3.class;
            Constructor<LazySingleton3> constructor = clazz.getDeclaredConstructor();
            sington3 = constructor.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        } 
        
        System.out.println("getInstance() : "+sington);
        //getInstance() : com.blueStarWei.pattern.LazySingleton3@15db9742
        
        System.out.println("getInstance() : "+sington2);
        //getInstance() : com.blueStarWei.pattern.LazySingleton3@15db9742
        
        System.out.println("invoke : "+sington3);
        //invoke : com.blueStarWei.pattern.LazySingleton3@6d06d69c
    }
}

 

 【在分布式系统中,单例类有时需要实现Serializable接口。以上版本通过序列和反序列化产生的实例将会不是同个实例对象,所以产生了懒汉模式4.0版本】

 

  2.7 懒汉模式4.0版本

    2.7.1 Demo代码

package com.blueStarWei.pattern;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;

public class LazySingleton4Demo2 {

    public static void main(String[] args) {
        
        LazySingleton4 singleton = LazySingleton4.getInstance();
        try {
            ObjectOutput out = new ObjectOutputStream(new FileOutputStream("fileName.txt"));
            out.writeObject(singleton);
            out.close();
            //deserializable from file to object
            ObjectInput in = new ObjectInputStream(new FileInputStream("fileName.txt"));
            LazySingleton4 singleton2 = (LazySingleton4)in.readObject();
            in.close();
            
            System.out.println("Serializable : "+singleton);
                        
            System.out.println("Deserializable : "+singleton2);
            
        } catch (Exception e) {
            e.printStackTrace();
        } 
        
    }
}

 

  2.7.2 反序列返回不同的对象示例

public class LazySingleton4 implements Serializable

        2.7.2.1 Demo日志

Serializable : com.blueStarWei.pattern.LazySingleton4@33909752
Deserializable : com.blueStarWei.pattern.LazySingleton4@776ec8df

 

  2.7.3 版本代码

package com.blueStarWei.pattern;

import java.io.Serializable;

public class LazySingleton4 implements Serializable{
    
    private static final long serialVersionUID = 1L;
    
    private static boolean initialized = false;
    
    public LazySingleton4() {
        synchronized (LazySingleton4.class) {
            if(initialized == false){
                initialized = !initialized;
            }else{
                throw new RuntimeException("Singleton has been broken!");
            }
        }
    }
    
    static class SingletonHolder {
        private static final LazySingleton4 singleton4 = new LazySingleton4();
    }
        
    public static LazySingleton4 getInstance() {
        return SingletonHolder.singleton4;
    }
    
    /* ----添加部分 start---- */
    /**
     * 反序列化的结果仍然通过getInstance()获取
     */
    private Object readResolve(){
        return getInstance();
    }
    /* ----添加部分 end---- */
}

        2.7.3.1 Demo日志

Serializable : com.blueStarWei.pattern.LazySingleton4@33909752
Deserializable : com.blueStarWei.pattern.LazySingleton4@33909752

 

3 总结

   实际项目中,一般从懒汉模式2.0、3.0、4.0版本中选择一种即可

 

         更多内容,请访问:http://www.cnblogs.com/BlueStarWei/

设计模式之单例模式

标签:lang   targe   分布   额外   method   阻塞   print   2.4.1   been   

原文地址:https://www.cnblogs.com/BlueStarWei/p/8997932.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!