单例,Singleton

单例模式属于对象创建型模式,其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点。(一般有3种形式a:饥汉,b:懒汉,c:登记)
创建模式分类:A:工厂模式(Factory Model)B:单例模式(singleton pattern)C:原型模式(prototype)D:Builder模式
引:单例模式,我个人理解是对类的实例的创建的一种方式(手段),java中对象可以被显    示的或者是隐含的创建,即构造一个类的实

例.以下就以创建类的实例来说明单例的形式.

package com.model.singleton;
import java.lang.reflect.Constructor;
/**
 * 单例 (单例常使用的两种方式(饱汉,饥汉))
 * @author sw
 *
 */
//简单的单例(饥汉:在类第一次加载实例化一个实例,所以在使用时,花销的时间比较少,比较快。)
public class Singleton {
    private static Singleton instance = new Singleton();
    private  String id;
    //私有的构造方法
    private Singleton(){}
    public static Singleton getInstance(){
        return instance;
    }
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        //创建两个实例s1 s2
        Singleton s1=Singleton.getInstance();
        Singleton s2=Singleton.getInstance();
        Singleton s3=new Singleton();
        //判断两个实例是否相同
        System.out.println(s1==s2);
        s1.setId("singleton");
        //如果s1与s2不是同一个实例那么s2.getId()时是null即s3
        System.out.println("s2:"+s2+"  "+s2.getId()+"  "+"s1:"+s1);
        System.out.println("s3:"+s3+s3.getId());
        /**
         * 这里需要注意一个问题,就是通过反射每次都能获得类的新的实例,所以如果要使用单例,
         * 就不要使用反射创建类的实例对象。
         */
        try {
            Class c = Class.forName(Singleton.class.getName());
            //构造器
            Constructor ct = c.getDeclaredConstructor();
            ct.setAccessible(true);
            Singleton s4 = (Singleton)ct.newInstance();
            System.out.println("s4:"+s4+s4.getId());
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        //接着测试两种方式的性能问题
        Long startTime1 = System.currentTimeMillis();
        for(int i = 0 ;i<100000000;i++){
            Singleton instance = Singleton.getInstance();
        }
        Long endTime1 = System.currentTimeMillis();
        System.out.println(endTime1 - startTime1);
        //可以明显的看出饥汉比懒汉式单例性能上好,所以你使用了单例,如果在乎性能的话,请使用饥汉式。
    }
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
}


单例模式的要点:从项目角度出发A:是某个类只能有一个实例;B:是它必须自行创建这个实例;C:是它必须自行向整个系统提供这个实

例.从代码出发D:私有的构造方法E:指向自己实例的私有静态引用 F:已自己实例为返回值的静态的共有方法
优点:单例模式的优点:A:在内存中只有一个对像,节省内存空间B:避免频繁的创建销毁对象.C:避免对共享资源的多重调用D:可以全局

访问 使用场合(自定义,不大准确):A:需要频繁实例化然后销毁的对象。B:创建对象时耗时过多或者耗资源过多,但又经常用到的对

象。C:有状态的工具类对象(项目中有个Tools类,没有状态,不是singleton,类中的方法采用Static修饰直接是Tools.方法(),我个人

倾向static好,请大家各抒己见)。D:频繁访问数据库或文件的对象。以及其他我没用过的所有要求只有一个对象的场景.使Singleton

的缺陷:A:如何销毁这个实例,何时销毁,或者是解除它的职责,假设添加一个distory()方法,将this.singleton置为null,但此时系统

中的其它模块可能正持有这个实例的引用.随后在调用getInstance()方法,就 会产生例外一个新的实例,就会造成同时存在两个实例

的现象.B:效率问题,每次调用都会进行if()条件语句的判断C:不能继承,从Singleton派生出的类不是Singleton,如果其想要成为

Singleton就必须增加所必须的static函数和变量
 

package com.model.singleton;
/**
 *
 * @author sw
 *
 */
//懒汉模式(该单例方式,只有在第一次调用getInstance()时才会初始化一个实例,
//所以在第一次使用时会花销一定的时间,同时需要同步,同样会花销一定的时间,这样性能上会比第一种要差点)
public class SingletonModel {
    private static SingletonModel singletonModel = null;
    private String name;
    //私有的构造方法
    private SingletonModel() { }
    //加上了synchronized()
    public synchronized static SingletonModel getInstance(){
      if(null == singletonModel) {
            singletonModel = new SingletonModel();
        }
        return singletonModel;
    }
    public static void main(String[] args) {
        //测试两种方式性能问题(时间长的很长的原因的是加了synchronized(线程安全随后会讲解
        //单例模式的线程安全性))去掉synchronized时间也不如饥汉方式
        Long startTime = System.currentTimeMillis();
        for(int i = 0 ;i<100000000;i++){
            SingletonModel instance = SingletonModel.getInstance();
        }
        Long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
package com.model.singleton;mport java.util.HashMap;import java.util.Map;
/**
 * 登记式单例类
 * @author sw
 *
 */
public class SingletonRegister {
    private static  Map<String, SingletonRegister> map=new HashMap<String, SingletonRegister>();
    static{      
        SingletonRegister single = new SingletonRegister();      
        map.put(single.getClass().getName(), single);
        }
    private String test;
    //保护的默认构造函数
    protected SingletonRegister(){}
    //静态工厂方法,返还此类惟一的实例
    public static SingletonRegister getInstance(String name) {
        if(name == null) {
            name = SingletonRegister.class.getName();
            System.out.println("name == null"+"--->name="+name);
            }
        if(map.get(name) == null) {
            try {
                map.put(name, (SingletonRegister) Class.forName(name).newInstance());           
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }      
            }
        System.out.println(map.get(name));
        return map.get(name);
    }
    //一个任意的方法
    public String about() {  
        return "Hello, I am RegSingleton.";  
    }   
    public static void main(String[] args) {
        SingletonRegister s1 = SingletonRegister.getInstance(null);
        //方法可能不准确
        System.out.println(s1.about());
        //再次用属性去校验
        SingletonRegister s2 = SingletonRegister.getInstance(null);
        s2.setTest("wo shi s2 set");
        SingletonRegister s3 = SingletonRegister.getInstance(null);
        System.out.println(s3.getTest());
    }
    public String getTest() {
        return test;
    }
    public void setTest(String test) {
        this.test = test;
    }
}
package com.model.singleton;
/**
 * 单例在多线程的环境下
 * @author sw
 *
 */
public class SingletonThread {
    private  static  SingletonThread sThread=new SingletonThread();
    private String name;
    private SingletonThread(){ }
    public static SingletonThread getSingletonThread(){
        return sThread;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
package com.model.singleton;
public class Test {
    public static void main(String[] args) {
        ThreadTest t1=new ThreadTest();Thread ta=new Thread(t1);
        ta.setName("ta");Thread tb=new Thread(t1); tb.setName("tb"); ta.start(); tb.start();
    }
}
package com.model.singleton;
/**
 * 创建两个线程
 * @author sw
 *
 */
public class ThreadTest implements Runnable {
    //3个线程
    @SuppressWarnings("static-access")
    @Override
    public void run() {
        SingletonModel s3=SingletonModel.getInstance();
        Thread t1=Thread.currentThread();
        SingletonModel s4 =SingletonModel.getInstance();
        s3.setName("李四"); System.out.println(s4.getName());
    }
    public  synchronized void method(){
        //饥汉方式是线程安全的
        /**原因是:当多个线程同时去访问该类的getInstance()方法时
         * 不会初始化多个不同的对象,这是因为,JVM在加载此类时对于static属性的
         * 初始化只能由一个线程执行而且仅一次(static属性和static初始化块的初始化
         * 是串行的)
         */
        SingletonThread s1=SingletonThread.getSingletonThread();
        SingletonThread s2=SingletonThread.getSingletonThread();
        s1.setName("张三");
        System.out.println(s2.getName());
        //懒汉方式;//此处我没有想出来两个线程如何同时执行run()
        //假设两个线程同时执行的话,Thread1与Thread2都会进入创建单例的代码块分别创建实例,在
        /**
         * if(null == singletonModel){
            singletonModel = new SingletonModel();
            }
         */
        /*处Thread1创建了一个实例对象,Thread2此时无法知道,于是它也会创建一个实例,最终两个线程所持有的
         * 实例不是同一个,如果java没有垃圾回收机制的话,会因为忽略其它线程创建的实例.而引起内存的泄漏
         * 解决方案是在Instance方法加上synchronized此时的性能问题刚才大家都看到了
         * "很差"!!!!
         * 原因:只要保证创建实例的逻辑代码被一个线程执行就ok而返回引用的那段代码是没有必要同步的,
         * 根据这种思路,以下代码可以改为:SingletonSyThread
         * public synchronized static SingletonModel getInstance(){
                if(null == singletonModel){
                    singletonModel = new SingletonModel();
                }
                return singletonModel;
            }
         * 
         */
        SingletonModel s3=SingletonModel.getInstance();
        SingletonModel s4=SingletonModel.getInstance();
        s3.setName("李四");
        System.out.println(s4.getName());
    }
}
package com.model.singleton;
/**
 *
 * @author sw
 *此程序只有在java5及以上版本才能正常执行,因为java平台的内存模式容许out-of-oorder writes引起
 *假定有两个线程Thread1和Thread2,他们的执行步骤如下:
 *
 *还有另外一种方式LazyLoadedSingleton
 */
public class SingletonSyThread {
    private volatile static SingletonSyThread singletonSyThread=null;
    public static SingletonSyThread getInstance(){
        if(singletonSyThread==null){//在此处不管哪一个线程先占据同步锁创建实例对象,都不会组织另外一个线程继续进入实

例代码块重新创建实例对象,这样
            //同样会生成两个实例对象.所以我们需要在同步的代码块里,进行第二次的判断,此时是同步的
            synchronized (SingletonSyThread.class) {//同步创建块
                if (singletonSyThread==null) {//再次检查,如果它是否被创建Double_Check Locking
                    //因为属性singletonSyThread是被volatile修饰的.因为volatile具有synchronized的可见性特点
                    //也就是说随后进来的线程能够发现volatile变量的最新值,
                    singletonSyThread=new SingletonSyThread();
                }
            }
        }
        return singletonSyThread;
    }
}
package com.model.singleton;import java.io.Serializable;
/**
 * Singleton 的序列化
 * @author sw
 *注意事项:如果单例实现了Serializable 接口.在默认的情况下,每次反序列化都会产生一个新的实例对象,
 *解决方式:readResolve()在反序列化出来执行之前执行的方法,我们在这个方法中,将我们创建的实例替换掉
 *反序列化出来的那个实例,让它指向内存中的那个单例对象即可
 */
public class SingletonSeria implements Serializable {
    private static final long serialVersionUID = -5569630403365107116L;
    private static SingletonSeria seria=new SingletonSeria();
    private SingletonSeria(){   
    }
    private Object readResolve(){
        return seria;
    }
    public static SingletonSeria getInstance(){
        return seria;
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值