zoukankan      html  css  js  c++  java
  • 设计模式(Java语言)-单例模式

      单例模式,简而言之就是在整个应用程序里面有且仅有一个实例,在程序的任何时候,任何地方获取到的该对象都是同一个对象。单例模式解决了一个全局的类被频繁创建和销毁的,或者每次创建或销毁都需要消耗大量cpu资源的对象的问题。单例模式总的可以分为懒汉模式和饿汉模式,顾名思义,懒汉模式是一个非常懒的汉子,只要你没有使用到它,它就永远不会实例化。饿汉模式的意思就是,汉子非常饥渴,只要在程序的编译阶段就给你分配内存,创建好对象。

      将懒汉模式和饿汉模式细分,又可以分为:

      1、懒汉模式

      2、饿汉模式

      3、双检模式

      4、静态内部类模式

      5、枚举模式

      不管是用哪一种方式实现的单例模式,其创建流程基本都是一直的:首先将构造方法声明为private的,这样就防止直接new出一个新的对象。第二,声明一个私有的成员变量,即单例对象。第三步,声明一个public的静态方法,用于获取或创建单例对象,外部想要获取该对象必须通过这个方法获取。

      一、懒汉模式1--线程安全

    /**
     * 饿汉模式1
     */
    public class HungrySingleton1 {
    
        private static HungrySingleton1 singleton = new HungrySingleton1();
    
        private HungrySingleton1(){}
    
        public static HungrySingleton1 getInstance() {
            return singleton;
        }
    
    
    }
    

      这种懒汉模式的优点是实现非常简单。缺点是并起到懒加载的效果,如果项目没有使用到这个对象的就会造成资源的浪费。

      二、饿汉模式1--线程不安全

    /**
     * 懒汉模式1
     */
    public class LazySingleton1 {
    
        private static LazySingleton1 singleton;
    
        private LazySingleton1(){}
    
        public static LazySingleton1 getInstance() {
            if (singleton == null) {
                singleton = new LazySingleton1();
            }
            return singleton;
        }
    
    
    }
    

      这种写法虽然实现了懒加载的效果,但是严格意义上并不是单例模式,因为在多线程的环境下有可能会创建出多个不同的对象,至于为什么,不懂的可以看一下我之间写的关于Java内存模型的文章。这种写法只能应用于单线程的环境下,局限性很大。实际中强烈不建议使用这种方法。

      三、懒汉模式2--线程安全

      

    /**
     * 懒汉模式2
     */
    public class LazySingleton2 {
    
        private static LazySingleton2 singleton;
    
        private LazySingleton2(){}
    
        public static synchronized LazySingleton2 getInstance() {
            if (singleton == null) {
                singleton = new LazySingleton2();
            }
            return singleton;
        }
    
    
    }
    

      这种写法咋看跟上面的方法一样,这种写法在方法上添加了 synchronized  关键字,这样就保证了每次只能有一个线程进入方法体中,解决了懒汉模式1中出现的问题。这种写法的优点是实现了懒加载的效果,缺点是效率非常低,当多个线程同时获取实例时,有可能会造成线程阻塞的情况。不推荐使用。

      懒汉模式3--线程不安全

    /**
     * 懒汉模式3
     */
    public class LazySingleton3 {
    
        private static LazySingleton3 singleton;
    
        private LazySingleton3(){}
    
        public static LazySingleton3 getInstance() {
            if (singleton == null) {
                synchronized (LazySingleton3.class) {
                    if (singleton == null) {
                        singleton = new LazySingleton3();
                    }
                }
            }
            return singleton;
        }
    }
    

      这种写法进行了两次 singleton == null 的判断,在实际的应用中当我们调用这个方法时,其实99%的几率是实例就已经创建好了,因此第一个 singleton == null 能过滤掉99%的调用,不用将方法锁起来,从而提高了效率。这种方法的优点是实现懒加载的效果,效率和很高。缺点是代码设计仍然后缺陷,jvm在为对象分配内存和赋值并不是一个原子操作,即 singleton = new LazySingleton3() 这段代码在jvm中是由三个步骤实现的,首先jvm会在堆中为对象分配一定的内存空间,然后完成对象的初始化工作,然后将内存地址指向到对象中。但是,我们知道,jvm在编译的时候并不总是根据我们编写的代码的顺序来执行了,而是根据jvm觉得最优的顺序执行(这个过程就叫做指令重排序),所以有可能在执行了步骤1后就执行了步骤3,这时候第二个线程进来的发现singleton并不为空,因此就直接返回了该对象,因此造成空指针异常。

      四、双重检查锁模式---线程安全

    /**
     * 懒汉模式4
     */
    public class LazySingleton4 {
    
        private volatile static LazySingleton4 singleton;
    
        private LazySingleton4(){}
    
        public static LazySingleton4 getInstance() {
            if (singleton == null) {
                synchronized (LazySingleton4.class) {
                    if (singleton == null) {
                        singleton = new LazySingleton4();
                    }
                }
            }
            return singleton;
        }
    }
    

      相较于上面的方式,这种方式只是在成员变量中添加了 volatile  关键字,解决了指令重排序的问题,同时确保当前线程修改了这个变量时,其他的线程能够及时读到最新的值。这种方法缺点是写起来比较复杂,要求程序员对jvm比较理解。优点是既保证了线程安全,同时也能够保证了比较高的效率。

      五、静态内部类模式--线程安全

    /**
     * 懒汉模式5
     */
    public class LazySingleton5 {
    
        private LazySingleton5(){}
    
        private static class Holder {
            private static final LazySingleton5 INSTANCE = new LazySingleton5();
        }
    
        public static LazySingleton5 getInstance() {
            return Holder.INSTANCE;
        }
    
    }
    

      这种写法实现比较简单,即实现了懒加载的效果,同时也保证的多线程环境下的线程安全问题。推荐使用这种方式。

      六、枚举模式 -- 线程安全

      

    /**
     * 懒汉模式6
     */
    public enum  LazySingleton6 {
    
       INSTANCE
    
    }

    //使用方法
    public class Test {

    public static void main(String[] args) {
    LazySingleton6 instance = LazySingleton6.INSTANCE;
    LazySingleton6 instance1 = LazySingleton6.INSTANCE;
    System.out.println(instance == instance1);

    }

    }

      推荐写法,简单高效。充分利用枚举类的特性,只定义了一个实例,且枚举类是天然支持多线程的。

      喜欢我写的博客的同学可以关注订阅号【Java解忧杂货铺】,里面不定期发布一些技术干活,也可以免费获取大量最新最流行的技术教学视频

      

     

     

  • 相关阅读:
    HDU 1069 Monkey and Banana
    HDU 1029 Ignatius and the Princess IV
    HDU 1024 Max Sum Plus Plus
    Gym100923H Por Costel and the Match
    Codeforces 682C Alyona and the Tree
    Codeforces 449B Jzzhu and Cities
    Codeforces (ccpc-wannafly camp day2) L. Por Costel and the Semipalindromes
    Codeforces 598D (ccpc-wannafly camp day1) Igor In the Museum
    Codeforces 1167c(ccpc wannafly camp day1) News Distribution 并查集模板
    快乐数问题
  • 原文地址:https://www.cnblogs.com/rainple/p/11886524.html
Copyright © 2011-2022 走看看