精华内容
下载资源
问答
  • JAVA设计模式之单例模式

    万次阅读 多人点赞 2014-04-16 06:51:34
    本文继续介绍23种设计模式系列之单例模式。 概念:  java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。  单例模式有以下特点:  1、单例类...

    本文继续介绍23种设计模式系列之单例模式。

    概念:
      java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。
      单例模式有以下特点:
      1、单例类只能有一个实例。
      2、单例类必须自己创建自己的唯一实例。
      3、单例类必须给所有其他对象提供这一实例。
      单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。


    一、懒汉式单例

     

    //懒汉式单例类.在第一次调用的时候实例化自己 
    public class Singleton {
        private Singleton() {}
        private static Singleton single=null;
        //静态工厂方法 
        public static Singleton getInstance() {
             if (single == null) {  
                 single = new Singleton();
             }  
            return single;
        }
    }

     

    Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。

    (事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。)

    但是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全,如果你第一次接触单例模式,对线程安全不是很了解,可以先跳过下面这三小条,去看饿汉式单例,等看完后面再回头考虑线程安全的问题:

     

    1、在getInstance方法上加同步

     

    public static synchronized Singleton getInstance() {
             if (single == null) {  
                 single = new Singleton();
             }  
            return single;
    }

     

     

     

    2、双重检查锁定

     

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

     

    3、静态内部类

     

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

    这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。

     

     

     

     

     

     

    二、饿汉式单例

     

    //饿汉式单例类.在类初始化时,已经自行实例化 
    public class Singleton1 {
        private Singleton1() {}
        private static final Singleton1 single = new Singleton1();
        //静态工厂方法 
        public static Singleton1 getInstance() {
            return single;
        }
    }

    饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。

     

     

     

     

    三、登记式单例(可忽略)

    //类似Spring里面的方法,将类名注册,下次从里面直接获取。
    public class Singleton3 {
        private static Map<String,Singleton3> map = new HashMap<String,Singleton3>();
        static{
            Singleton3 single = new Singleton3();
            map.put(single.getClass().getName(), single);
        }
        //保护的默认构造子
        protected Singleton3(){}
        //静态工厂方法,返还此类惟一的实例
        public static Singleton3 getInstance(String name) {
            if(name == null) {
                name = Singleton3.class.getName();
                System.out.println("name == null"+"--->name="+name);
            }
            if(map.get(name) == null) {
                try {
                    map.put(name, (Singleton3) Class.forName(name).newInstance());
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
            return map.get(name);
        }
        //一个示意性的商业方法
        public String about() {    
            return "Hello, I am RegSingleton.";    
        }    
        public static void main(String[] args) {
            Singleton3 single3 = Singleton3.getInstance(null);
            System.out.println(single3.about());
        }
    }

     登记式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。 

    这里我对登记式单例标记了可忽略,我的理解来说,首先它用的比较少,另外其实内部实现还是用的饿汉式单例,因为其中的static方法块,它的单例在类被装载的时候就被实例化了。

     

    饿汉式和懒汉式区别

    从名字上来说,饿汉和懒汉,

    饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,

    而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

    另外从以下两点再区分以下这两种方式:

     

    1、线程安全:

    饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,

    懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的1、2、3,这三种实现在资源加载和性能方面有些区别。


     

    2、资源加载和性能:

    饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,

    而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

    至于1、2、3这三种实现又有些区别,

    第1种,在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的,

    第2种,在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗

    第3种,利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗,所以一般我倾向于使用这一种。

     

    什么是线程安全?

    如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

    或者说:一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,那就是线程安全的。

     

    应用

    以下是一个单例类使用的例子,以懒汉式为例,这里为了保证线程安全,使用了双重检查锁定的方式:

     

    public class TestSingleton {
    	String name = null;
    
            private TestSingleton() {
    	}
    
    	private static volatile TestSingleton instance = null;
    
    	public static TestSingleton getInstance() {
               if (instance == null) {  
                 synchronized (TestSingleton.class) {  
                    if (instance == null) {  
                       instance = new TestSingleton(); 
                    }  
                 }  
               } 
               return instance;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public void printInfo() {
    		System.out.println("the name is " + name);
    	}
    
    }

    可以看到里面加了volatile关键字来声明单例对象,既然synchronized已经起到了多线程下原子性、有序性、可见性的作用,为什么还要加volatile呢,原因已经在下面评论中提到,

    还有疑问可参考http://www.iteye.com/topic/652440
    和http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

     

     

     

    public class TMain {
    	public static void main(String[] args){
    		TestStream ts1 = TestSingleton.getInstance();
    		ts1.setName("jason");
    		TestStream ts2 = TestSingleton.getInstance();
    		ts2.setName("0539");
    		
    		ts1.printInfo();
    		ts2.printInfo();
    		
    		if(ts1 == ts2){
    			System.out.println("创建的是同一个实例");
    		}else{
    			System.out.println("创建的不是同一个实例");
    		}
    	}
    }
    

     运行结果:

    结论:由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。

    对于单例模式的几种实现方式,知道饿汉式和懒汉式的区别,线程安全,资源加载的时机,还有懒汉式为了实现线程安全的3种方式的细微差别。

    更多设计模式:23种设计模式系列

    作者:jason0539

    博客:http://blog.csdn.net/jason0539(转载请说明出处)

    展开全文
  • 单例

    2019-09-18 21:29:52
    两种单例 Java中有两种实现单例的模式,一种是饿汉式,一种是懒汉式。饿汉式是从一开始就创建单例对象,懒汉式是什么时候需要这个单例对象就去找这个单例对象,当发现没有时再创建。 1.饿汉式 /********* ...
                                                两种单例
    
    
       Java中有两种实现单例的模式,一种是饿汉式,一种是懒汉式。饿汉式是从一开始就创建单例对象,懒汉式是什么时候需要这个单例对象就去找这个单例对象,当发现没有时再创建。
    

    1.饿汉式
    饿汉式单例在类加载初始化时就创建好一个静态的对象供外部使用,除非系统重启,这个对象不会改变,所以本身就是线程安全的。

    Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,
    Singleton的唯一实例只能通过getInstance()方法访问。(
    事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。
    此问题在此处不做讨论,姑且闭着眼就认为反射机制不存在。)

    public class Ehan {

    private static int in = 1;
    private static Ehan ehan = new Ehan();
    
    //私有的构造方法避免了new 出本对象,只能通过getInstance方法获得本对象
    private Ehan(){
    	
    }
    public static Ehan getInstance(){
    	int in = 2;
    	
    	return ehan;
    }
    public static int getIn() {
    	return in;
    }
    public static void setIn(int in) {
    	Ehan.in = in;
    }
    

    }

    2.懒汉式

    • 该示例虽然用延迟加载方式实现了懒汉式单例,但在多线程环境下会产生多个single对象,如何改造请看以下方式: 使用synchronized同步锁

    public class Lanhan {

    private Lanhan() {
    
    }
    
    private static Lanhan lanhan = new Lanhan();
    
    public Lanhan getInstance() {
    
    	if (lanhan == null) {
    		synchronized (Lanhan.class) {
    			if (lanhan == null) {
    				lanhan = new Lanhan();
    			}
    		}
    	}
    	return lanhan;
    
    }
    

    }

    最后:
    类的构造器为私有(private)的话是不能被继承的。

    展开全文
  • 细数单例模式

    万次阅读 2020-09-18 11:29:59
    为什么要有单例模式 单例模式是一个类对外仅提供一个实例对象,防止出现对象的不一致状态,与多例对象是对立的 单例模式有以下特点  1、单例类只能有一个实例。  2、单例类必须自己创建自己的唯一实例。  3...

    为什么要有单例模式

    单例模式是一个类对外仅提供一个实例对象,防止出现对象的不一致状态,与多例对象是对立的

     单例模式有以下特点

      1、单例类只能有一个实例。
      2、单例类必须自己创建自己的唯一实例。
      3、单例类必须给所有其他对象提供这一实例。

    单例模式的有点

    1. 只有一个对象,内存开支少、性能好

    2. 避免对资源的多重占用

    3. 在系统设置全局访问点,优化和共享资源访问

    单例的写法 

     饿汉模式

    不管对象会不会被使用,都提前实例化 好对象,等待被使用

     1、是线程安全的

      2、因为在类文件加载之初就会实例化对象,如果最终该对象没有被使用到会造成一定程度的内存浪费(针对目前的服务器能力无伤大雅)

    
    /**
     * @Description: <饿汉式>
     * @Author: milla
     * @CreateDate: 2020/09/18 10:33
     * @UpdateUser: milla
     * @UpdateDate: 2020/09/18 10:33
     * @UpdateRemark: <>
     * @Version: 1.0
     */
    public class Singleton {
    
        /**
         * 内部实例化
         */
        private final static Singleton instance = new Singleton();
    
        /**
         * 私有化构造-阻止其他再次实例化该对象
         */
        private Singleton() {
        }
    
        /**
         * 对外提供获取对象的方式
         *
         * @return
         */
        public static Singleton getInstance() {
            return instance;
        }
    }

     懒汉式

    先声明出来对象,但是并不做实例化,当被调用的时候才会去实例化

    1、线程不安全(可通过改版进行线程安全改造)

    2、按需实例化,更符合节约资源的思想

    
    /**
     * @Description: <懒汉式>
     * @Author: milla
     * @CreateDate: 2020/09/18 10:33
     * @UpdateUser: milla
     * @UpdateDate: 2020/09/18 10:33
     * @UpdateRemark: <>
     * @Version: 1.0
     */
    public class SingletonLazy {
    
        /**
         * 内部实例化
         */
        private static SingletonLazy instance;
    
        /**
         * 私有化构造
         */
        private SingletonLazy() {
        }
    
        /**
         * 对外提供获取对象的方式
         *
         * @return
         */
        public static SingletonLazy getInstance() {
            if (instance == null) {
                instance = new SingletonLazy();
            }
            return instance;
        }
    }

     上述类,在多线程情况下很容易出现线程安全问题,如果对方法直接加同步锁(synchronized),是可以达到线程安全的目的,但是会大大降低程序执行的性能,因为获取对象的时候都会形成阻塞

     懒汉式改版-方法上加同步关键字

    1、线程安全

    2、但是因为线程会形成阻塞队列,因此会牺牲性能

    
    /**
     * @Description: <懒汉式>
     * @Author: milla
     * @CreateDate: 2020/09/18 10:33
     * @UpdateUser: milla
     * @UpdateDate: 2020/09/18 10:33
     * @UpdateRemark: <>
     * @Version: 1.0
     */
    public class SingletonLazy {
    
        /**
         * 内部实例化
         */
        private static SingletonLazy instance;
    
        /**
         * 私有化构造
         */
        private SingletonLazy() {
        }
    
        /**
         * 对外提供获取对象的方式
         *
         * @return
         */
        public static synchronized SingletonLazy getInstance() {
            if (instance == null) {
                instance = new SingletonLazy();
            }
            return instance;
        }
    }

    饿汉式改版-双重加锁判空并防止指令重排 

     

    
    /**
     * @Description: <懒汉式>
     * @Author: milla
     * @CreateDate: 2020/09/18 10:33
     * @UpdateUser: milla
     * @UpdateDate: 2020/09/18 10:33
     * @UpdateRemark: <>
     * @Version: 1.0
     */
    public class SingletonLazy {
    
        /**
         * 内部实例化
         */
        private static volatile SingletonLazy instance;
    
        /**
         * 私有化构造
         */
        private SingletonLazy() {
        }
    
        /**
         * 对外提供获取对象的方式
         *
         * @return
         */
        public static SingletonLazy getInstance() {
            //如果对象已经被实例化就不用阻塞
            if (instance == null) {
                synchronized (SingletonLazy.class) {
                    //防止多个线程都判断实例对象为空,然后形成阻塞队列,重复实例对象
                    if (instance == null) {
    //                    JVM新建对象的时候,会经过三个步骤
    //                    1.分配内存
    //                    2.初始化构造器
    //                    3.将对象指向分配的内存的地址
    //                    PS:2和3可能会出现指令重排,导致重复创建对象,因此对象要要使用volatile关键字组织JVM指令重排
                        instance = new SingletonLazy();
                    }
                }
            }
            return instance;
        }
    }

    静态内部类 

    1、线程安全

    2、静态变量只会加载一次,在初始化时JVM会强行保证同步,所以能保证实例是单例且是线程安全的

    
    /**
     * @Description: <静态内部类>
     * @Author: milla
     * @CreateDate: 2020/09/18 11:12
     * @UpdateUser: milla
     * @UpdateDate: 2020/09/18 11:12
     * @UpdateRemark: <>
     * @Version: 1.0
     */
    public class Singleton {
        public static Singleton getInstance() {
            return SingletonInner.instance;
        }
    
        /**
         * 私有化
         */
        private Singleton() {
        }
    
        /**
         * 静态内部类
         * 静态变量只会加载一次,在初始化时JVM会强行保证同步,所以能保证实例是单例且是线程安全的
         */
        private static class SingletonInner {
            /**
             * 内部实例化
             */
            protected final static Singleton instance = new Singleton();
        }
    }

    枚举模式 

    
    /**
     * @Description: <枚举>
     * @Author: milla
     * @CreateDate: 2020/09/18 11:24
     * @UpdateUser: milla
     * @UpdateDate: 2020/09/18 11:24
     * @UpdateRemark: <>
     * @Version: 1.0
     */
    public enum Singleton {
        /**
        * 实例对象
        */
        SINGLETON;
    
        /**
         * 实例要执行的方法
         */
        public void doSomething() {
            //业务逻辑在枚举类中
        }
    }
    

    单例模式实现案例 

    用于用户登录表单用户名和密码及登录之后token的本地线程存储

    
    /**
     * @Description: <单例存储用户登录前后信息>
     * @Author: milla
     * @CreateDate: 2020/09/11 17:48
     * @UpdateUser: milla
     * @UpdateDate: 2020/09/11 17:48
     * @UpdateRemark: <>
     * @Version: 1.0
     */
    public enum AuthenticationStoreUtil {
        AUTHENTICATION;
        /**
         * 登录认证之后的token(每个请求都要验证token是否非法)
         */
        private final ThreadLocal<String> tokenStore = new ThreadLocal<>();
        /**
         * 登录时需要验证用户名
         */
        private final ThreadLocal<String> usernameStore = new ThreadLocal<>();
        /**
         * 登录时需要验证的密码
         */
        private final ThreadLocal<String> passwordStore = new ThreadLocal<>();
    
        public static String getUsername() {
            return AUTHENTICATION.usernameStore.get();
        }
    
        public static void setUsername(String username) {
            AUTHENTICATION.usernameStore.set(username);
        }
    
        public static String getPassword() {
            return AUTHENTICATION.passwordStore.get();
        }
    
        public static void setPassword(String password) {
            AUTHENTICATION.passwordStore.set(password);
        }
    
        public static String getToken() {
            return AUTHENTICATION.tokenStore.get();
        }
    
        public static void setToken(String token) {
            AUTHENTICATION.tokenStore.set(token);
        }
    
        /**
         * 清除所有登录信息
         */
        public static void clear() {
            AUTHENTICATION.tokenStore.remove();
            AUTHENTICATION.passwordStore.remove();
            AUTHENTICATION.usernameStore.remove();
        }
    }
    

      PS : 从代码量上看,最好用的应该是枚举和静态内部类,并发系统中需要考虑线程的安全问题

    展开全文
  • 深入理解单例模式:静态内部类单例原理

    万次阅读 多人点赞 2018-05-26 02:00:54
    本文主要介绍java的单例模式,以及详细剖析静态内部类之所以能够实现单例的原理。OK,废话不多说,进入正文。 首先我们要先了解下单例的四大原则: 1.构造私有。 2.以静态方法或者枚举返回实例。 3.确保实例只有...

    本文主要介绍java的单例模式,以及详细剖析静态内部类之所以能够实现单例的原理。OK,废话不多说,进入正文。

    首先我们要先了解下单例的四大原则:

    1.构造私有。
    2.以静态方法或者枚举返回实例。

    3.确保实例只有一个,尤其是多线程环境。

    4.确保反序列换时不会重新构建对象。

    我们常用的单例模式有:

    饿汉模式、懒汉模式、双重锁懒汉模式、静态内部类模式、枚举模式,我们来逐一分析下这些模式的区别。

    1.饿汉模式:

    public class SingleTon{
       private static SingleTon INSTANCE = new SingleTon();
     private SingleTon(){}
    public static SingleTon getInstance(){ return INSTANCE; }}

    饿汉模式在类被初始化时就已经在内存中创建了对象,以空间换时间,故不存在线程安全问题。

    2.懒汉模式:

    public class SingleTon{
       private static SingleTon  INSTANCE = null;
       private SingleTon(){}
       public static SingleTon getInstance() {  
       if(INSTANCE == null){
          INSTANCE = new SingleTon(); 
        } 
        return INSTANCE;
      }
    }

    懒汉模式在方法被调用后才创建对象,以时间换空间,在多线程环境下存在风险

    3.双重锁懒汉模式(Double Check Lock)

    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();
           } 
         } 
            return INSTANCE; 
        } 
      }
    }

     

    DCL模式的优点就是,只有在对象需要被使用时才创建,第一次判断 INSTANCE == null为了避免非必要加锁,当第一次加载时才对实例进行加锁再实例化。这样既可以节约内存空间,又可以保证线程安全。但是,由于jvm存在乱序执行功能,DCL也会出现线程不安全的情况。具体分析如下:

     

     

    INSTANCE  = new SingleTon(); 

    这个步骤,其实在jvm里面的执行分为三步:

              1.在堆内存开辟内存空间。
      2.在堆内存中实例化SingleTon里面的各个参数。
      3.把对象指向堆内存空间。

    由于jvm存在乱序执行功能,所以可能在2还没执行时就先执行了3,如果此时再被切换到线程B上,由于执行了3,INSTANCE 已经非空了,会被直接拿出来用,这样的话,就会出现异常。这个就是著名的DCL失效问题

    不过在JDK1.5之后,官方也发现了这个问题,故而具体化了volatile,即在JDK1.6及以后,只要定义为private volatile static SingleTon  INSTANCE = null;就可解决DCL失效问题。volatile确保INSTANCE每次均在主内存中读取,这样虽然会牺牲一点效率,但也无伤大雅。

    3.静态内部类模式:

    public class SingleTon{
      private SingleTon(){}
    
      private static class SingleTonHoler{
         private static SingleTon INSTANCE = new SingleTon();
     }
    
      public static SingleTon getInstance(){
        return SingleTonHoler.INSTANCE;
      }
    }

    静态内部类的优点是:外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE,故而不占内存。即当SingleTon第一次被加载时,并不需要去加载SingleTonHoler,只有当getInstance()方法第一次被调用时,才会去初始化INSTANCE,第一次调用getInstance()方法会导致虚拟机加载SingleTonHoler类,这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。

    那么,静态内部类又是如何实现线程安全的呢?首先,我们先了解下类的加载时机。


    类加载时机:JAVA虚拟机在有且仅有的5种场景下会对类进行初始化。
    1.遇到new、getstatic、setstatic或者invokestatic这4个字节码指令时,对应的java代码场景为:new一个关键字或者一个实例化对象时、读取或设置一个静态字段时(final修饰、已在编译期把结果放入常量池的除外)、调用一个类的静态方法时。
    2.使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没进行初始化,需要先调用其初始化方法进行初始化。
    3.当初始化一个类时,如果其父类还未进行初始化,会先触发其父类的初始化。
    4.当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的类),虚拟机会先初始化这个类。
    5.当使用JDK 1.7等动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
    这5种情况被称为是类的主动引用,注意,这里《虚拟机规范》中使用的限定词是"有且仅有",那么,除此之外的所有引用类都不会对类进行初始化,称为被动引用。静态内部类就属于被动引用的行列。

     

    我们再回头看下getInstance()方法,调用的是SingleTonHoler.INSTANCE,取的是SingleTonHoler里的INSTANCE对象,跟上面那个DCL方法不同的是,getInstance()方法并没有多次去new对象,故不管多少个线程去调用getInstance()方法,取的都是同一个INSTANCE对象,而不用去重新创建。当getInstance()方法被调用时,SingleTonHoler才在SingleTon的运行时常量池里,把符号引用替换为直接引用,这时静态对象INSTANCE也真正被创建,然后再被getInstance()方法返回出去,这点同饿汉模式。那么INSTANCE在创建过程中又是如何保证线程安全的呢?在《深入理解JAVA虚拟机》中,有这么一句话:

     虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。如果在一个类的<clinit>()方法中有耗时很长的操作,就可能造成多个进程阻塞(需要注意的是,其他线程虽然会被阻塞,但如果执行<clinit>()方法后,其他线程唤醒之后不会再次进入<clinit>()方法。同一个加载器下,一个类型只会初始化一次。),在实际应用中,这种阻塞往往是很隐蔽的。

    故而,可以看出INSTANCE在创建过程中是线程安全的,所以说静态内部类形式的单例可保证线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。

    那么,是不是可以说静态内部类单例就是最完美的单例模式了呢?其实不然,静态内部类也有着一个致命的缺点,就是传参的问题,由于是静态内部类的形式去创建单例的,故外部无法传递参数进去,例如Context这种参数,所以,我们创建单例时,可以在静态内部类与DCL模式里自己斟酌。

    最后粗略的介绍下枚举类型的单例吧。

    枚举单例:

    public enum SingleTon{
      INSTANCE;
            public void method(){
            //TODO
         }
    }

    枚举在java中与普通类一样,都能拥有字段与方法,而且枚举实例创建是线程安全的,在任何情况下,它都是一个单例。我们可直接以

    SingleTon.INSTANCE

    的方式调用。

    参考资料:
    《深入理解JAVA虚拟机》
    《Android源码设计模式解析与实战》
    《java虚拟机规范》

     

     

    展开全文
  • 单例模式

    万次阅读 2018-12-25 16:49:30
    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不...
  • 单例模式的五种写法

    万次阅读 多人点赞 2019-06-23 13:52:48
    其中接下来我们要写的是单例模式,属于创建型模式。 单例模式,顾名思义就是只有一个实例,并且她自己负责创建自己的对象,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。下面...
  • 设计模式-单例模式

    万次阅读 2019-12-02 22:26:53
    目录 知识点1:设计模式分类 知识点2:设计模式的原则 1、开闭原则(Open Close Principle) 2、里氏代换原则(Liskov Substitution Principle) 3、依赖倒转原则(Dependence Inversion Principle) ...
  • java单例模式

    万次阅读 多人点赞 2018-05-25 14:59:26
     本文首先概述了单例模式产生动机,揭示了单例模式的本质和应用场景。紧接着,我们给出了单例模式在单线程环境下的两种经典实现:饿汉式和懒汉式,但是饿汉式是线程安全的,而懒汉式是非线程安全的。在多线程环境下...
  • 单例模式几种写法

    万次阅读 2020-08-15 20:52:36
    单例模式的七种写法 转载请注明出处:http://cantellow.iteye.com/blog/838473 第一种(懒汉,线程不安全): Java代码 收藏代码 public class Singleton { private static Singleton instance; private ...
  • 单例模式饿汉式与懒汉式,内部类实现单例模式

    万次阅读 多人点赞 2020-11-08 19:17:07
    单例模式 单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对持有一个实例,并提供一个全局访问点。 饿汉式单例模式 就是在类加载的时候就立即初始化,并且创建单例对象。绝对的线程安全 public class ...
  • SpringMVC Controller单例和多例

    万次阅读 多人点赞 2017-03-29 03:28:47
    对于SpringMVC Controller单例和多例,下面举了个例子说明下. 第一次:类是多例,一个普通属性和一个静态属性。 结果:普通属性:0.............静态属性:0 普通属性:0.............静态属性:1 普通属性:0......
  • Java实现单例的5种方式

    万次阅读 多人点赞 2018-04-01 00:07:35
    Java实现单例的5种方式 1. 什么是单例模式 单例模式指的是在应用整个生命周期内只能存在一个实例。单例模式是一种被广泛使用的设计模式。他有很多好处,能够避免实例对象的重复创建,减少创建实例的系统开销,...
  • 单例模式讨论篇:单例模式与垃圾回收

    万次阅读 多人点赞 2012-03-08 09:14:35
    Jvm的垃圾回收机制到底会不会回收掉长时间不用的单例模式对象,这的确是一个比较有争议性的问题。将这一部分内容单独成篇的目的也是为了与广大博友广泛的讨论一下这个问题。为了能让更多的人看到这篇文章,请各位...
  • 什么是单例? 单例的意思是一个类永远只存在一个对象, 不能创建多个对象。 为什么要用单例? 开发中有很多类的对象我们只需要一个, 例如虚拟机对象、任务管理器对象 对象越多越占内存,有些时候只需要一个对象 就...
  • 主要介绍了浅谈Spring单例Bean与单例模式的区别,具有一定借鉴价值,需要的朋友可以参考下
  • 单例模式的特点: 1.单例模式只有一个实例 2.单例类必须自己创建自己唯一的实例 3.单例类必须给其他对象提供这一对象实例 懒汉式单例 特点: 1.懒汉式单例在第一次调用的时候要初始化 2.懒汉式单例的线程不安全,在...
  • ios 单例模式详解

    千次阅读 2018-10-31 12:51:14
    单例
  • 单例传说

    2020-01-08 07:40:00
    单例传说 在设计模式这个门派里面,有个很常见的招式,叫做单例派。 什么是单例 所谓单例,顾名思义,就是单个实例,使用单例模式可以让某个类全局只有一个实例。 单例的好处 对类的唯一实例做出访问控制允许...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 116,157
精华内容 46,462
关键字:

单例