网站首页 设计模式正文

单例模式【java版】

单例模式【java版】

一、基本特点

      如何防止创建多个实例:构造方法设置为私有,使得外部无法直接new出实例

      基本组成:a)一个静态的和自身类型相同的成员对象

                      b)私有的构造方法

                      c)获取实例的公有方法,供外部调用,以返回实例

       

二、懒汉式

特点:类被加载时不创建实例,getInstance方法第一次被调用时才创建实例

类代码:

public class LazySingleton {
       
    /**
     * 静态的和自身类型相同的成员对象
     */
    private static LazySingleton theLazySingleton=null;
    
    /**
     * 构造方法设置为私有,保证无法从外部new出实例
     * LazySingleton myLazySingleton=new LazySingleton();这样的语句就无法通过编译
     */
    private LazySingleton(){}
    
    /**
     * 供外部调用的获取实例的方法,会在第一次调用时初始化实例
     */
    public static LazySingleton getInstance(){
        
        if(theLazySingleton==null){
            System.out.println("懒汉式单例,第一次调用,先创建,再返回实例!");
            theLazySingleton=new LazySingleton();    
        }else{
            System.out.println("懒汉式单例,已不是第一次调用,直接返回实例!");
        }
        
        return theLazySingleton;
    }
    
}

主函数:

public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println("开始测试单例模式!");
        
        //尽管使用了两个引用名,实际上指向的是内存中同一个实例
        LazySingleton myLazySingleton_A, myLazySingleton_B;
        
        myLazySingleton_A=LazySingleton.getInstance();
        myLazySingleton_B=LazySingleton.getInstance();                
    }

运行结果:

三、饿汉式

特点:类被加载时就创建实例

类代码:

public class HungrySingleton {
    
    private static final HungrySingleton theHungrySingleton=new HungrySingleton();
    
    private HungrySingleton(){}
    
    public static HungrySingleton getInstance(){
        
        System.out.println("饿汉式单例,实例已在类加载时被创建,故可直接返回实例!");
        
        return theHungrySingleton;
    }
    
}

主函数:

public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println("开始测试单例模式!");    
        
        HungrySingleton myHungrySingleton=HungrySingleton.getInstance();
    }

运行结果:

四、多线程环境下的单例模式

    A)线程安全的单例:

  1、效率较高线程安全单例(常用方式
            “静态内部类”只在getInstance()方法第一次调用的时候才会被加载(实现了lazy),而且其加载过程是线程安全的(实现线程安全)

//静态内部类实现懒汉式  
public class Singleton {  
      
    private static class SingletonHolder{  
        //单例变量    
        private static Singleton instance = new Singleton();  
    }  
      
    //私有化的构造方法,保证外部的类不能通过构造器来实例化。  
    private Singleton() {  
          
    }  
      
    //获取单例对象实例  
    public static Singleton getInstance() {  
        System.out.println("我是内部类单例!");  
        return SingletonHolder.instance;  
    }  
}

2、效率较低线程安全单例
            a)懒汉式,静态方法getInstance前加“类锁”
                缺点:每次获取实例时都会对类进行加锁操作,影响性能

         实际只需第一次instance==null时加类锁,防止内存中创建了两个实例。

public class Singleton {  
      
    //单例实例变量  
    private static Singleton instance = null;  
      
    //私有化的构造方法,保证外部的类不能通过构造器来实例化  
    private Singleton() {}  
      
    //获取单例对象实例  
    public static synchronized  Singleton getInstance() {  
          
        if (instance == null) {   
            instance = new Singleton();   
        }  
          
        System.out.println("我是同步法懒汉式单例!");  
        return instance;  
    }  
}

 b)饿汉式
                 缺点: 一些未必需要加载的模块会每次都被加载,影响整个系统初次加载速度

public class Singleton {  
      
    //单例变量 ,static的,在类加载时进行初始化一次,保证线程安全   
    private static Singleton instance = new Singleton();      
      
    //私有化的构造方法,保证外部的类不能通过构造器来实例化。       
    private Singleton() {}  
      
    //获取单例对象实例       
    public static Singleton getInstance() {  
        System.out.println("我是饿汉式单例!");  
        return instance;  
    }  
}

B) java“写无序”导致的“非线程安全单例”:双重锁定懒汉式

//双重锁定懒汉式  
public class Singleton {  
      
    //单例实例变量  
    private static Singleton instance = null;  
      
    //私有化的构造方法,保证外部的类不能通过构造器来实例化  
    private Singleton() {}  
      
    //获取单例对象实例  
    public static Singleton getInstance() {  
        if (instance == null) {   
            synchronized (Singleton.class) {  
                if (instance == null) {   
                    instance = new Singleton();   
                }  
            }  
        }  
        System.out.println("我是双重锁定懒汉式单例!");  
        return instance;  
    }  
}

 Java内存模型中的“无序写”(out-of-order writes)机制,

  可能导致:intance<>null时,只是intance=mem,而mem=allocate()还没有完成。

  所谓的“无序写”就是,单条语句instance=new Singleton()

  实际执行时为两条语句:1、mem=new Singleton()

             2、instance=mem

  而这两个操作是无序的

  PS:整条语句执行完,肯定instance中的值是正确的,但两步中间可能有其他线程进来访问instance。


还没有人评论?赶快抢个沙发~

发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。